Compare commits
34
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ee0cf0659d | ||
|
|
d1fade919c | ||
|
|
b2eb85fdf1 | ||
|
|
d8138fbe69 | ||
|
|
e54dd191f1 | ||
|
|
db632e2522 | ||
|
|
52e1dfe99f | ||
|
|
3a99c2154c | ||
|
|
241312b0be | ||
|
|
aef117bcf9 | ||
|
|
582b2c43cb | ||
|
|
900951b1a7 | ||
|
|
fc647890a6 | ||
|
|
86fa27c78d | ||
|
|
268eca39c0 | ||
|
|
88e3767971 | ||
|
|
cdeea60e52 | ||
|
|
f1ae24f627 | ||
|
|
eb34e6692f | ||
|
|
d003f67273 | ||
|
|
faa9a5e9d5 | ||
|
|
196e877711 | ||
|
|
a347175e53 | ||
|
|
f443a817e6 | ||
|
|
c507dc6923 | ||
|
|
22b0dc4e4c | ||
|
|
7f4a6cbf75 | ||
|
|
599f0972af | ||
|
|
495bc6e06f | ||
|
|
41b1197b23 | ||
|
|
cbadfd7095 | ||
|
|
536e213b57 | ||
|
|
5fbc4a88ec | ||
|
|
581734d740 |
@@ -1,80 +1,80 @@
|
|||||||
name: Deploy Promiscuity Auth API
|
name: Deploy Promiscuity Auth API
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: promiscuity-auth:latest
|
IMAGE_NAME: promiscuity-auth:latest
|
||||||
IMAGE_TAR: /tmp/promiscuity-auth.tar
|
IMAGE_TAR: /tmp/promiscuity-auth.tar
|
||||||
# All nodes that might run the pod (control-plane + workers)
|
# All nodes that might run the pod (control-plane + workers)
|
||||||
NODES: "192.168.86.72 192.168.86.73 192.168.86.74"
|
NODES: "192.168.86.72 192.168.86.73 192.168.86.74"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Build Docker image
|
# Build Docker image
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: |
|
run: |
|
||||||
cd microservices/AuthApi
|
cd microservices/AuthApi
|
||||||
docker build -t "${IMAGE_NAME}" .
|
docker build -t "${IMAGE_NAME}" .
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Save image as TAR on runner
|
# Save image as TAR on runner
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Save Docker image to TAR
|
- name: Save Docker image to TAR
|
||||||
run: |
|
run: |
|
||||||
docker save "${IMAGE_NAME}" -o "${IMAGE_TAR}"
|
docker save "${IMAGE_NAME}" -o "${IMAGE_TAR}"
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Copy TAR to each Kubernetes node
|
# Copy TAR to each Kubernetes node
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Copy TAR to nodes
|
- name: Copy TAR to nodes
|
||||||
run: |
|
run: |
|
||||||
for node in ${NODES}; do
|
for node in ${NODES}; do
|
||||||
echo "Copying image tar to $node ..."
|
echo "Copying image tar to $node ..."
|
||||||
scp -o StrictHostKeyChecking=no "${IMAGE_TAR}" hz@"$node":/tmp/promiscuity-auth.tar
|
scp -o StrictHostKeyChecking=no "${IMAGE_TAR}" hz@"$node":/tmp/promiscuity-auth.tar
|
||||||
done
|
done
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Import image into containerd on each node
|
# Import image into containerd on each node
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Import image on nodes
|
- name: Import image on nodes
|
||||||
run: |
|
run: |
|
||||||
for node in ${NODES}; do
|
for node in ${NODES}; do
|
||||||
echo "Importing image on $node ..."
|
echo "Importing image on $node ..."
|
||||||
ssh -o StrictHostKeyChecking=no hz@"$node" "sudo ctr -n k8s.io images import /tmp/promiscuity-auth.tar"
|
ssh -o StrictHostKeyChecking=no hz@"$node" "sudo ctr -n k8s.io images import /tmp/promiscuity-auth.tar"
|
||||||
done
|
done
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# CLEANUP: delete TAR from nodes
|
# CLEANUP: delete TAR from nodes
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Clean TAR from nodes
|
- name: Clean TAR from nodes
|
||||||
run: |
|
run: |
|
||||||
for node in ${NODES}; do
|
for node in ${NODES}; do
|
||||||
echo "Removing image tar on $node ..."
|
echo "Removing image tar on $node ..."
|
||||||
ssh -o StrictHostKeyChecking=no hz@"$node" "rm -f /tmp/promiscuity-auth.tar"
|
ssh -o StrictHostKeyChecking=no hz@"$node" "rm -f /tmp/promiscuity-auth.tar"
|
||||||
done
|
done
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# CLEANUP: delete TAR from runner
|
# CLEANUP: delete TAR from runner
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Clean TAR on runner
|
- name: Clean TAR on runner
|
||||||
run: |
|
run: |
|
||||||
rm -f "${IMAGE_TAR}"
|
rm -f "${IMAGE_TAR}"
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Write kubeconfig from secret
|
# Write kubeconfig from secret
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Write kubeconfig from secret
|
- name: Write kubeconfig from secret
|
||||||
env:
|
env:
|
||||||
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
||||||
@@ -82,23 +82,32 @@ jobs:
|
|||||||
mkdir -p /tmp/kube
|
mkdir -p /tmp/kube
|
||||||
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Ensure namespace exists
|
||||||
|
# -----------------------------
|
||||||
|
- name: Create namespace if missing
|
||||||
|
env:
|
||||||
|
KUBECONFIG: /tmp/kube/config
|
||||||
|
run: |
|
||||||
|
kubectl create namespace promiscuity-auth --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Apply Kubernetes manifests
|
# Apply Kubernetes manifests
|
||||||
# (You create these files in your repo)
|
# (You create these files in your repo)
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Apply Auth deployment & service
|
- name: Apply Auth deployment & service
|
||||||
env:
|
env:
|
||||||
KUBECONFIG: /tmp/kube/config
|
KUBECONFIG: /tmp/kube/config
|
||||||
run: |
|
run: |
|
||||||
kubectl apply -f microservices/AuthApi/k8s/deployment.yaml -n promiscuity-auth
|
kubectl apply -f microservices/AuthApi/k8s/deployment.yaml -n promiscuity-auth
|
||||||
kubectl apply -f microservices/AuthApi/k8s/service.yaml -n promiscuity-auth
|
kubectl apply -f microservices/AuthApi/k8s/service.yaml -n promiscuity-auth
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Rollout restart & wait
|
# Rollout restart & wait
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Restart Auth deployment
|
- name: Restart Auth deployment
|
||||||
env:
|
env:
|
||||||
KUBECONFIG: /tmp/kube/config
|
KUBECONFIG: /tmp/kube/config
|
||||||
run: |
|
run: |
|
||||||
kubectl rollout restart deployment/promiscuity-auth -n promiscuity-auth
|
kubectl rollout restart deployment/promiscuity-auth -n promiscuity-auth
|
||||||
kubectl rollout status deployment/promiscuity-auth -n promiscuity-auth
|
kubectl rollout status deployment/promiscuity-auth -n promiscuity-auth
|
||||||
|
|||||||
@@ -1,80 +1,80 @@
|
|||||||
name: Deploy Promiscuity Character API
|
name: Deploy Promiscuity Character API
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
workflow_dispatch: {}
|
workflow_dispatch: {}
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
deploy:
|
deploy:
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
|
|
||||||
env:
|
env:
|
||||||
IMAGE_NAME: promiscuity-character:latest
|
IMAGE_NAME: promiscuity-character:latest
|
||||||
IMAGE_TAR: /tmp/promiscuity-character.tar
|
IMAGE_TAR: /tmp/promiscuity-character.tar
|
||||||
# All nodes that might run the pod (control-plane + workers)
|
# All nodes that might run the pod (control-plane + workers)
|
||||||
NODES: "192.168.86.72 192.168.86.73 192.168.86.74"
|
NODES: "192.168.86.72 192.168.86.73 192.168.86.74"
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Build Docker image
|
# Build Docker image
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Build Docker image
|
- name: Build Docker image
|
||||||
run: |
|
run: |
|
||||||
cd microservices/CharacterApi
|
cd microservices/CharacterApi
|
||||||
docker build -t "${IMAGE_NAME}" .
|
docker build -t "${IMAGE_NAME}" .
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Save image as TAR on runner
|
# Save image as TAR on runner
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Save Docker image to TAR
|
- name: Save Docker image to TAR
|
||||||
run: |
|
run: |
|
||||||
docker save "${IMAGE_NAME}" -o "${IMAGE_TAR}"
|
docker save "${IMAGE_NAME}" -o "${IMAGE_TAR}"
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Copy TAR to each Kubernetes node
|
# Copy TAR to each Kubernetes node
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Copy TAR to nodes
|
- name: Copy TAR to nodes
|
||||||
run: |
|
run: |
|
||||||
for node in ${NODES}; do
|
for node in ${NODES}; do
|
||||||
echo "Copying image tar to $node ..."
|
echo "Copying image tar to $node ..."
|
||||||
scp -o StrictHostKeyChecking=no "${IMAGE_TAR}" hz@"$node":/tmp/promiscuity-character.tar
|
scp -o StrictHostKeyChecking=no "${IMAGE_TAR}" hz@"$node":/tmp/promiscuity-character.tar
|
||||||
done
|
done
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Import image into containerd on each node
|
# Import image into containerd on each node
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Import image on nodes
|
- name: Import image on nodes
|
||||||
run: |
|
run: |
|
||||||
for node in ${NODES}; do
|
for node in ${NODES}; do
|
||||||
echo "Importing image on $node ..."
|
echo "Importing image on $node ..."
|
||||||
ssh -o StrictHostKeyChecking=no hz@"$node" "sudo ctr -n k8s.io images import /tmp/promiscuity-character.tar"
|
ssh -o StrictHostKeyChecking=no hz@"$node" "sudo ctr -n k8s.io images import /tmp/promiscuity-character.tar"
|
||||||
done
|
done
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# CLEANUP: delete TAR from nodes
|
# CLEANUP: delete TAR from nodes
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Clean TAR from nodes
|
- name: Clean TAR from nodes
|
||||||
run: |
|
run: |
|
||||||
for node in ${NODES}; do
|
for node in ${NODES}; do
|
||||||
echo "Removing image tar on $node ..."
|
echo "Removing image tar on $node ..."
|
||||||
ssh -o StrictHostKeyChecking=no hz@"$node" "rm -f /tmp/promiscuity-character.tar"
|
ssh -o StrictHostKeyChecking=no hz@"$node" "rm -f /tmp/promiscuity-character.tar"
|
||||||
done
|
done
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# CLEANUP: delete TAR from runner
|
# CLEANUP: delete TAR from runner
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Clean TAR on runner
|
- name: Clean TAR on runner
|
||||||
run: |
|
run: |
|
||||||
rm -f "${IMAGE_TAR}"
|
rm -f "${IMAGE_TAR}"
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Write kubeconfig from secret
|
# Write kubeconfig from secret
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Write kubeconfig from secret
|
- name: Write kubeconfig from secret
|
||||||
env:
|
env:
|
||||||
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
||||||
@@ -83,21 +83,30 @@ jobs:
|
|||||||
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Apply Kubernetes manifests
|
# Ensure namespace exists
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Apply Character deployment & service
|
- name: Create namespace if missing
|
||||||
env:
|
env:
|
||||||
KUBECONFIG: /tmp/kube/config
|
KUBECONFIG: /tmp/kube/config
|
||||||
run: |
|
run: |
|
||||||
kubectl apply -f microservices/CharacterApi/k8s/deployment.yaml -n promiscuity-character
|
kubectl create namespace promiscuity-character --dry-run=client -o yaml | kubectl apply -f -
|
||||||
kubectl apply -f microservices/CharacterApi/k8s/service.yaml -n promiscuity-character
|
|
||||||
|
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
# Rollout restart & wait
|
# Apply Kubernetes manifests
|
||||||
# -----------------------------
|
# -----------------------------
|
||||||
- name: Restart Character deployment
|
- name: Apply Character deployment & service
|
||||||
env:
|
env:
|
||||||
KUBECONFIG: /tmp/kube/config
|
KUBECONFIG: /tmp/kube/config
|
||||||
run: |
|
run: |
|
||||||
kubectl rollout restart deployment/promiscuity-character -n promiscuity-character
|
kubectl apply -f microservices/CharacterApi/k8s/deployment.yaml -n promiscuity-character
|
||||||
kubectl rollout status deployment/promiscuity-character -n promiscuity-character
|
kubectl apply -f microservices/CharacterApi/k8s/service.yaml -n promiscuity-character
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Rollout restart & wait
|
||||||
|
# -----------------------------
|
||||||
|
- name: Restart Character deployment
|
||||||
|
env:
|
||||||
|
KUBECONFIG: /tmp/kube/config
|
||||||
|
run: |
|
||||||
|
kubectl rollout restart deployment/promiscuity-character -n promiscuity-character
|
||||||
|
kubectl rollout status deployment/promiscuity-character -n promiscuity-character
|
||||||
|
|||||||
@@ -0,0 +1,112 @@
|
|||||||
|
name: Deploy Promiscuity Locations API
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
workflow_dispatch: {}
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
deploy:
|
||||||
|
runs-on: self-hosted
|
||||||
|
|
||||||
|
env:
|
||||||
|
IMAGE_NAME: promiscuity-locations:latest
|
||||||
|
IMAGE_TAR: /tmp/promiscuity-locations.tar
|
||||||
|
# All nodes that might run the pod (control-plane + workers)
|
||||||
|
NODES: "192.168.86.72 192.168.86.73 192.168.86.74"
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repo
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Build Docker image
|
||||||
|
# -----------------------------
|
||||||
|
- name: Build Docker image
|
||||||
|
run: |
|
||||||
|
cd microservices/LocationsApi
|
||||||
|
docker build -t "${IMAGE_NAME}" .
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Save image as TAR on runner
|
||||||
|
# -----------------------------
|
||||||
|
- name: Save Docker image to TAR
|
||||||
|
run: |
|
||||||
|
docker save "${IMAGE_NAME}" -o "${IMAGE_TAR}"
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Copy TAR to each Kubernetes node
|
||||||
|
# -----------------------------
|
||||||
|
- name: Copy TAR to nodes
|
||||||
|
run: |
|
||||||
|
for node in ${NODES}; do
|
||||||
|
echo "Copying image tar to $node ..."
|
||||||
|
scp -o StrictHostKeyChecking=no "${IMAGE_TAR}" hz@"$node":/tmp/promiscuity-locations.tar
|
||||||
|
done
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Import image into containerd on each node
|
||||||
|
# -----------------------------
|
||||||
|
- name: Import image on nodes
|
||||||
|
run: |
|
||||||
|
for node in ${NODES}; do
|
||||||
|
echo "Importing image on $node ..."
|
||||||
|
ssh -o StrictHostKeyChecking=no hz@"$node" "sudo ctr -n k8s.io images import /tmp/promiscuity-locations.tar"
|
||||||
|
done
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# CLEANUP: delete TAR from nodes
|
||||||
|
# -----------------------------
|
||||||
|
- name: Clean TAR from nodes
|
||||||
|
run: |
|
||||||
|
for node in ${NODES}; do
|
||||||
|
echo "Removing image tar on $node ..."
|
||||||
|
ssh -o StrictHostKeyChecking=no hz@"$node" "rm -f /tmp/promiscuity-locations.tar"
|
||||||
|
done
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# CLEANUP: delete TAR from runner
|
||||||
|
# -----------------------------
|
||||||
|
- name: Clean TAR on runner
|
||||||
|
run: |
|
||||||
|
rm -f "${IMAGE_TAR}"
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Write kubeconfig from secret
|
||||||
|
# -----------------------------
|
||||||
|
- name: Write kubeconfig from secret
|
||||||
|
env:
|
||||||
|
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
||||||
|
run: |
|
||||||
|
mkdir -p /tmp/kube
|
||||||
|
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Ensure namespace exists
|
||||||
|
# -----------------------------
|
||||||
|
- name: Create namespace if missing
|
||||||
|
env:
|
||||||
|
KUBECONFIG: /tmp/kube/config
|
||||||
|
run: |
|
||||||
|
kubectl create namespace promiscuity-locations --dry-run=client -o yaml | kubectl apply -f -
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Apply Kubernetes manifests
|
||||||
|
# -----------------------------
|
||||||
|
- name: Apply Locations deployment & service
|
||||||
|
env:
|
||||||
|
KUBECONFIG: /tmp/kube/config
|
||||||
|
run: |
|
||||||
|
kubectl apply -f microservices/LocationsApi/k8s/deployment.yaml -n promiscuity-locations
|
||||||
|
kubectl apply -f microservices/LocationsApi/k8s/service.yaml -n promiscuity-locations
|
||||||
|
|
||||||
|
# -----------------------------
|
||||||
|
# Rollout restart & wait
|
||||||
|
# -----------------------------
|
||||||
|
- name: Restart Locations deployment
|
||||||
|
env:
|
||||||
|
KUBECONFIG: /tmp/kube/config
|
||||||
|
run: |
|
||||||
|
kubectl rollout restart deployment/promiscuity-locations -n promiscuity-locations
|
||||||
|
kubectl rollout status deployment/promiscuity-locations -n promiscuity-locations
|
||||||
@@ -1,27 +1,27 @@
|
|||||||
name: k8s smoke test
|
name: k8s smoke test
|
||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches:
|
branches:
|
||||||
- main
|
- main
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: self-hosted
|
runs-on: self-hosted
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repo
|
- name: Checkout repo
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
- name: Write kubeconfig from secret
|
- name: Write kubeconfig from secret
|
||||||
env:
|
env:
|
||||||
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
KUBECONFIG_CONTENT: ${{ secrets.KUBECONFIG }}
|
||||||
run: |
|
run: |
|
||||||
mkdir -p /tmp/kube
|
mkdir -p /tmp/kube
|
||||||
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
printf '%s\n' "$KUBECONFIG_CONTENT" > /tmp/kube/config
|
||||||
|
|
||||||
- name: Test kubectl connectivity
|
- name: Test kubectl connectivity
|
||||||
env:
|
env:
|
||||||
KUBECONFIG: /tmp/kube/config
|
KUBECONFIG: /tmp/kube/config
|
||||||
run: |
|
run: |
|
||||||
kubectl get nodes --kubeconfig "${KUBECONFIG}"
|
kubectl get nodes --kubeconfig "${KUBECONFIG}"
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
# Promiscuity
|
||||||
|
|
||||||
|
## Microservices
|
||||||
|
- Auth Microservice Swagger: https://pauth.ranaze.com/swagger/index.html
|
||||||
|
- Character Microservice Swagger: https://pchar.ranaze.com/swagger/index.html
|
||||||
|
- Location Microservice Swagger: https://ploc.ranaze.com/swagger/index.html
|
||||||
|
|
||||||
|
- Microservices README: microservices/README.md
|
||||||
|
|
||||||
|
## Test users
|
||||||
|
- `SUPER/SUPER` - Super User
|
||||||
|
- `test1/test1` - Super User
|
||||||
|
- `test3/test3` - User
|
||||||
|
|
||||||
|
## Controls
|
||||||
|
- Move: WASD
|
||||||
|
- Jump: Space
|
||||||
|
- Interact (enter/exit car): E
|
||||||
|
- Flashlight: F
|
||||||
|
- Phone: Tab
|
||||||
|
- Pause menu: Esc
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
Auth Microservice swagger is accessible at https://pauth.ranaze.com/swagger/index.html
|
|
||||||
|
|
||||||
Test Users:
|
|
||||||
SUPER/SUPER - Super User
|
|
||||||
test1/test1 - Super User
|
|
||||||
test3/test3 - User
|
|
||||||
|
|
||||||
+16
-16
@@ -1,17 +1,17 @@
|
|||||||
# Godot 4+ specific ignores
|
# Godot 4+ specific ignores
|
||||||
.godot/
|
.godot/
|
||||||
.nomedia
|
.nomedia
|
||||||
|
|
||||||
# Godot-specific ignores
|
# Godot-specific ignores
|
||||||
.import/
|
.import/
|
||||||
export.cfg
|
export.cfg
|
||||||
export_credentials.cfg
|
export_credentials.cfg
|
||||||
*.tmp
|
*.tmp
|
||||||
|
|
||||||
# Imported translations (automatically generated from CSV files)
|
# Imported translations (automatically generated from CSV files)
|
||||||
*.translation
|
*.translation
|
||||||
|
|
||||||
# Mono-specific ignores
|
# Mono-specific ignores
|
||||||
.mono/
|
.mono/
|
||||||
data_*/
|
data_*/
|
||||||
mono_crash.*.json
|
mono_crash.*.json
|
||||||
@@ -1,30 +1,30 @@
|
|||||||
# weapon.gd
|
# weapon.gd
|
||||||
extends Resource
|
extends Resource
|
||||||
class_name iWeapon
|
class_name iWeapon
|
||||||
# This acts as an interface base class.
|
# This acts as an interface base class.
|
||||||
|
|
||||||
# --- Common Stats ---
|
# --- Common Stats ---
|
||||||
@export var weapon_name: String = "Unnamed Weapon"
|
@export var weapon_name: String = "Unnamed Weapon"
|
||||||
@export var damage: float = 10.0
|
@export var damage: float = 10.0
|
||||||
@export var attack_speed: float = 1.0 # attacks per second
|
@export var attack_speed: float = 1.0 # attacks per second
|
||||||
@export var range: float = 1.5 # meters; melee = short, ranged = long
|
@export var range: float = 1.5 # meters; melee = short, ranged = long
|
||||||
@export var knockback: float = 0.0
|
@export var knockback: float = 0.0
|
||||||
@export var stamina_cost: float = 5.0
|
@export var stamina_cost: float = 5.0
|
||||||
|
|
||||||
# --- Ranged‑specific Stats ---
|
# --- Ranged‑specific Stats ---
|
||||||
@export var projectile_scene: PackedScene # null for melee
|
@export var projectile_scene: PackedScene # null for melee
|
||||||
@export var projectile_speed: float = 20.0
|
@export var projectile_speed: float = 20.0
|
||||||
@export var ammo_type: String = "" # e.g. "arrows", "bullets"
|
@export var ammo_type: String = "" # e.g. "arrows", "bullets"
|
||||||
@export var ammo_per_shot: int = 1
|
@export var ammo_per_shot: int = 1
|
||||||
|
|
||||||
# --- Melee‑specific Stats ---
|
# --- Melee‑specific Stats ---
|
||||||
@export var swing_arc: float = 90.0 # degrees
|
@export var swing_arc: float = 90.0 # degrees
|
||||||
@export var hitbox_size: float = 1.0
|
@export var hitbox_size: float = 1.0
|
||||||
|
|
||||||
# --- Interface Methods ---
|
# --- Interface Methods ---
|
||||||
func attack(user):
|
func attack(user):
|
||||||
push_error("Weapon.attack() not implemented in subclass")
|
push_error("Weapon.attack() not implemented in subclass")
|
||||||
return null
|
return null
|
||||||
|
|
||||||
func can_attack(user) -> bool:
|
func can_attack(user) -> bool:
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bttaq8w3plgqh
|
uid://bttaq8w3plgqh
|
||||||
|
|||||||
Binary file not shown.
@@ -0,0 +1,42 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://bnqaqbgynoyys"
|
||||||
|
path="res://.godot/imported/TestCharAnimated.glb-ecf7bb997b01494120b0dfccb4e1f064.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/models/TestCharAnimated.glb"
|
||||||
|
dest_files=["res://.godot/imported/TestCharAnimated.glb-ecf7bb997b01494120b0dfccb4e1f064.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=false
|
||||||
|
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={}
|
||||||
|
gltf/naming_version=2
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
Binary file not shown.
@@ -0,0 +1,42 @@
|
|||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="scene"
|
||||||
|
importer_version=1
|
||||||
|
type="PackedScene"
|
||||||
|
uid="uid://cpuebxqhtv72i"
|
||||||
|
path="res://.godot/imported/humanFemale.glb-45dfb97e0f5fa98244d8cb6255565c65.scn"
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://assets/models/humanFemale.glb"
|
||||||
|
dest_files=["res://.godot/imported/humanFemale.glb-45dfb97e0f5fa98244d8cb6255565c65.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=false
|
||||||
|
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={}
|
||||||
|
gltf/naming_version=2
|
||||||
|
gltf/embedded_image_handling=1
|
||||||
@@ -1,3 +1,3 @@
|
|||||||
[gd_scene format=3 uid="uid://cuyws13lbkmxb"]
|
[gd_scene format=3 uid="uid://cuyws13lbkmxb"]
|
||||||
|
|
||||||
[node name="Test" type="Node2D"]
|
[node name="Test" type="Node2D"]
|
||||||
|
|||||||
+12
-12
@@ -1,12 +1,12 @@
|
|||||||
[gd_resource type="AudioBusLayout" format=3]
|
[gd_resource type="AudioBusLayout" format=3]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
bus/0/name = "Master"
|
bus/0/name = "Master"
|
||||||
bus/0/volume_db = 0.0
|
bus/0/volume_db = 0.0
|
||||||
bus/0/send = ""
|
bus/0/send = ""
|
||||||
bus/1/name = "Music"
|
bus/1/name = "Music"
|
||||||
bus/1/volume_db = 0.0
|
bus/1/volume_db = 0.0
|
||||||
bus/1/send = "Master"
|
bus/1/send = "Master"
|
||||||
bus/2/name = "SFX"
|
bus/2/name = "SFX"
|
||||||
bus/2/volume_db = 0.0
|
bus/2/volume_db = 0.0
|
||||||
bus/2/send = "Master"
|
bus/2/send = "Master"
|
||||||
|
|||||||
+70
-70
@@ -1,70 +1,70 @@
|
|||||||
[preset.0]
|
[preset.0]
|
||||||
|
|
||||||
name="Windows Desktop"
|
name="Windows Desktop"
|
||||||
platform="Windows Desktop"
|
platform="Windows Desktop"
|
||||||
runnable=true
|
runnable=true
|
||||||
advanced_options=false
|
advanced_options=false
|
||||||
dedicated_server=false
|
dedicated_server=false
|
||||||
custom_features=""
|
custom_features=""
|
||||||
export_filter="all_resources"
|
export_filter="all_resources"
|
||||||
include_filter=""
|
include_filter=""
|
||||||
exclude_filter=""
|
exclude_filter=""
|
||||||
export_path="bin/test.exe"
|
export_path="bin/test.exe"
|
||||||
patches=PackedStringArray()
|
patches=PackedStringArray()
|
||||||
encryption_include_filters=""
|
encryption_include_filters=""
|
||||||
encryption_exclude_filters=""
|
encryption_exclude_filters=""
|
||||||
seed=0
|
seed=0
|
||||||
encrypt_pck=false
|
encrypt_pck=false
|
||||||
encrypt_directory=false
|
encrypt_directory=false
|
||||||
script_export_mode=2
|
script_export_mode=2
|
||||||
|
|
||||||
[preset.0.options]
|
[preset.0.options]
|
||||||
|
|
||||||
custom_template/debug=""
|
custom_template/debug=""
|
||||||
custom_template/release=""
|
custom_template/release=""
|
||||||
debug/export_console_wrapper=1
|
debug/export_console_wrapper=1
|
||||||
binary_format/embed_pck=false
|
binary_format/embed_pck=false
|
||||||
texture_format/s3tc_bptc=true
|
texture_format/s3tc_bptc=true
|
||||||
texture_format/etc2_astc=false
|
texture_format/etc2_astc=false
|
||||||
shader_baker/enabled=false
|
shader_baker/enabled=false
|
||||||
binary_format/architecture="x86_64"
|
binary_format/architecture="x86_64"
|
||||||
codesign/enable=false
|
codesign/enable=false
|
||||||
codesign/timestamp=true
|
codesign/timestamp=true
|
||||||
codesign/timestamp_server_url=""
|
codesign/timestamp_server_url=""
|
||||||
codesign/digest_algorithm=1
|
codesign/digest_algorithm=1
|
||||||
codesign/description=""
|
codesign/description=""
|
||||||
codesign/custom_options=PackedStringArray()
|
codesign/custom_options=PackedStringArray()
|
||||||
application/modify_resources=true
|
application/modify_resources=true
|
||||||
application/icon=""
|
application/icon=""
|
||||||
application/console_wrapper_icon=""
|
application/console_wrapper_icon=""
|
||||||
application/icon_interpolation=4
|
application/icon_interpolation=4
|
||||||
application/file_version=""
|
application/file_version=""
|
||||||
application/product_version=""
|
application/product_version=""
|
||||||
application/company_name=""
|
application/company_name=""
|
||||||
application/product_name=""
|
application/product_name=""
|
||||||
application/file_description=""
|
application/file_description=""
|
||||||
application/copyright=""
|
application/copyright=""
|
||||||
application/trademarks=""
|
application/trademarks=""
|
||||||
application/export_angle=0
|
application/export_angle=0
|
||||||
application/export_d3d12=0
|
application/export_d3d12=0
|
||||||
application/d3d12_agility_sdk_multiarch=true
|
application/d3d12_agility_sdk_multiarch=true
|
||||||
ssh_remote_deploy/enabled=false
|
ssh_remote_deploy/enabled=false
|
||||||
ssh_remote_deploy/host="user@host_ip"
|
ssh_remote_deploy/host="user@host_ip"
|
||||||
ssh_remote_deploy/port="22"
|
ssh_remote_deploy/port="22"
|
||||||
ssh_remote_deploy/extra_args_ssh=""
|
ssh_remote_deploy/extra_args_ssh=""
|
||||||
ssh_remote_deploy/extra_args_scp=""
|
ssh_remote_deploy/extra_args_scp=""
|
||||||
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
|
ssh_remote_deploy/run_script="Expand-Archive -LiteralPath '{temp_dir}\\{archive_name}' -DestinationPath '{temp_dir}'
|
||||||
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
|
$action = New-ScheduledTaskAction -Execute '{temp_dir}\\{exe_name}' -Argument '{cmd_args}'
|
||||||
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
|
$trigger = New-ScheduledTaskTrigger -Once -At 00:00
|
||||||
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
|
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries
|
||||||
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
|
$task = New-ScheduledTask -Action $action -Trigger $trigger -Settings $settings
|
||||||
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
|
Register-ScheduledTask godot_remote_debug -InputObject $task -Force:$true
|
||||||
Start-ScheduledTask -TaskName godot_remote_debug
|
Start-ScheduledTask -TaskName godot_remote_debug
|
||||||
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
|
while (Get-ScheduledTask -TaskName godot_remote_debug | ? State -eq running) { Start-Sleep -Milliseconds 100 }
|
||||||
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
|
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue"
|
||||||
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
|
ssh_remote_deploy/cleanup_script="Stop-ScheduledTask -TaskName godot_remote_debug -ErrorAction:SilentlyContinue
|
||||||
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
|
Unregister-ScheduledTask -TaskName godot_remote_debug -Confirm:$false -ErrorAction:SilentlyContinue
|
||||||
Remove-Item -Recurse -Force '{temp_dir}'"
|
Remove-Item -Recurse -Force '{temp_dir}'"
|
||||||
dotnet/include_scripts_content=false
|
dotnet/include_scripts_content=false
|
||||||
dotnet/include_debug_symbols=true
|
dotnet/include_debug_symbols=true
|
||||||
dotnet/embed_build_outputs=false
|
dotnet/embed_build_outputs=false
|
||||||
|
|||||||
+1
-1
@@ -1 +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>
|
<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>
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 996 B |
@@ -25,6 +25,7 @@ MenuMusic="*res://scenes/UI/menu_music.tscn"
|
|||||||
MenuSfx="*res://scenes/UI/menu_sfx.tscn"
|
MenuSfx="*res://scenes/UI/menu_sfx.tscn"
|
||||||
AuthState="*res://scenes/UI/auth_state.gd"
|
AuthState="*res://scenes/UI/auth_state.gd"
|
||||||
CharacterService="*res://scenes/UI/character_service.gd"
|
CharacterService="*res://scenes/UI/character_service.gd"
|
||||||
|
SelectedCharacter="*res://scenes/UI/selected_character.gd"
|
||||||
|
|
||||||
[dotnet]
|
[dotnet]
|
||||||
|
|
||||||
@@ -69,3 +70,13 @@ player_light={
|
|||||||
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":70,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
player_phone={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":0,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194306,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
interact={
|
||||||
|
"deadzone": 0.2,
|
||||||
|
"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":69,"key_label":0,"unicode":101,"location":0,"echo":false,"script":null)
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|||||||
+153
-144
@@ -1,156 +1,165 @@
|
|||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
@export var left_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeLeft/Pupil")
|
@export var left_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeLeft/Pupil")
|
||||||
@export var right_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeRight/Pupil")
|
@export var right_pupil_path: NodePath = NodePath("Body/HeadPivot/EyeRight/Pupil")
|
||||||
@export var camera_path: NodePath
|
@export var camera_path: NodePath
|
||||||
@export var look_origin_path: NodePath = NodePath("Body/HeadPivot")
|
@export var look_origin_path: NodePath = NodePath("Body/HeadPivot")
|
||||||
@export var look_reference_path: NodePath = NodePath("Body")
|
@export var look_reference_path: NodePath = NodePath("Body")
|
||||||
@export var lock_vertical: bool = true
|
@export var look_target_path: NodePath = NodePath("")
|
||||||
@export var vertical_unlock_height: float = 0.6
|
@export var lock_vertical: bool = true
|
||||||
@export var vertical_lock_smooth_speed: float = 6.0
|
@export var vertical_unlock_height: float = 0.6
|
||||||
@export var vertical_lock_hold_time: float = 0.3
|
@export var vertical_lock_smooth_speed: float = 6.0
|
||||||
@export var max_look_angle_deg: float = 90.0
|
@export var vertical_lock_hold_time: float = 0.3
|
||||||
@export var eye_return_speed: float = 0.2
|
@export var max_look_angle_deg: float = 90.0
|
||||||
@export var max_offset: float = 0.08
|
@export var eye_return_speed: float = 0.2
|
||||||
@export var side_eye_boost: float = 1.4
|
@export var max_offset: float = 0.08
|
||||||
@export var head_path: NodePath = NodePath("Body/HeadPivot")
|
@export var side_eye_boost: float = 1.4
|
||||||
@export var head_turn_speed: float = 16.0
|
@export var head_path: NodePath = NodePath("Body/HeadPivot")
|
||||||
@export var head_max_yaw_deg: float = 55.0
|
@export var head_turn_speed: float = 16.0
|
||||||
@export var head_max_pitch_deg: float = 22.0
|
@export var head_max_yaw_deg: float = 55.0
|
||||||
|
@export var head_max_pitch_deg: float = 22.0
|
||||||
var _left_pupil: Node3D
|
|
||||||
var _right_pupil: Node3D
|
var _left_pupil: Node3D
|
||||||
var _left_base: Vector3
|
var _right_pupil: Node3D
|
||||||
var _right_base: Vector3
|
var _left_base: Vector3
|
||||||
var _camera: Camera3D
|
var _right_base: Vector3
|
||||||
var _look_origin: Node3D
|
var _camera: Camera3D
|
||||||
var _head: Node3D
|
var _look_origin: Node3D
|
||||||
var _head_base_rot: Vector3
|
var _head: Node3D
|
||||||
var _vertical_lock_factor: float = 1.0
|
var _head_base_rot: Vector3
|
||||||
var _vertical_hold_timer: float = 0.0
|
var _vertical_lock_factor: float = 1.0
|
||||||
|
var _vertical_hold_timer: float = 0.0
|
||||||
var _look_reference: Node3D
|
var _look_reference: Node3D
|
||||||
|
var _look_target: Node3D
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_left_pupil = get_node_or_null(left_pupil_path) as Node3D
|
_left_pupil = get_node_or_null(left_pupil_path) as Node3D
|
||||||
_right_pupil = get_node_or_null(right_pupil_path) as Node3D
|
_right_pupil = get_node_or_null(right_pupil_path) as Node3D
|
||||||
if _left_pupil:
|
if _left_pupil:
|
||||||
_left_base = _left_pupil.position
|
_left_base = _left_pupil.position
|
||||||
if _right_pupil:
|
if _right_pupil:
|
||||||
_right_base = _right_pupil.position
|
_right_base = _right_pupil.position
|
||||||
_camera = _resolve_camera()
|
_camera = _resolve_camera()
|
||||||
_look_origin = get_node_or_null(look_origin_path) as Node3D
|
_look_origin = get_node_or_null(look_origin_path) as Node3D
|
||||||
_look_reference = get_node_or_null(look_reference_path) as Node3D
|
_look_reference = get_node_or_null(look_reference_path) as Node3D
|
||||||
|
_look_target = get_node_or_null(look_target_path) as Node3D
|
||||||
_head = get_node_or_null(head_path) as Node3D
|
_head = get_node_or_null(head_path) as Node3D
|
||||||
if _head:
|
if _head:
|
||||||
_head_base_rot = _head.rotation
|
_head_base_rot = _head.rotation
|
||||||
|
|
||||||
|
|
||||||
func _physics_process(_delta: float) -> void:
|
func _physics_process(_delta: float) -> void:
|
||||||
_update_pupils()
|
_update_pupils()
|
||||||
|
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
_update_pupils()
|
_update_pupils()
|
||||||
|
|
||||||
|
|
||||||
func _update_pupils() -> void:
|
func _update_pupils() -> void:
|
||||||
if _camera == null or not _camera.is_inside_tree():
|
if _look_target == null and look_target_path != NodePath(""):
|
||||||
_camera = _resolve_camera()
|
_look_target = get_node_or_null(look_target_path) as Node3D
|
||||||
if _camera == null:
|
if _look_target == null:
|
||||||
|
var viewport_cam := get_viewport().get_camera_3d()
|
||||||
|
if viewport_cam != null and viewport_cam != _camera:
|
||||||
|
_camera = viewport_cam
|
||||||
|
elif _camera == null or not _camera.is_inside_tree():
|
||||||
|
_camera = _resolve_camera()
|
||||||
|
if _look_target == null and _camera == null:
|
||||||
return
|
return
|
||||||
var origin := _look_origin
|
var origin := _look_origin
|
||||||
if origin == null:
|
if origin == null:
|
||||||
origin = self
|
origin = self
|
||||||
var target := _camera.global_position
|
var target := _look_target.global_position if _look_target != null else _camera.global_position
|
||||||
var dir_world := target - origin.global_position
|
var dir_world := target - origin.global_position
|
||||||
if dir_world.length_squared() <= 0.0001:
|
if dir_world.length_squared() <= 0.0001:
|
||||||
return
|
return
|
||||||
dir_world = dir_world.normalized()
|
dir_world = dir_world.normalized()
|
||||||
var reference := _look_reference if _look_reference != null else origin
|
var reference := _look_reference if _look_reference != null else origin
|
||||||
var forward := -reference.global_basis.z
|
var forward := -reference.global_basis.z
|
||||||
var min_dot := cos(deg_to_rad(max_look_angle_deg))
|
var min_dot := cos(deg_to_rad(max_look_angle_deg))
|
||||||
var can_look := dir_world.dot(forward) >= min_dot
|
var can_look := dir_world.dot(forward) >= min_dot
|
||||||
if not can_look:
|
if not can_look:
|
||||||
var delta := get_process_delta_time()
|
var delta := get_process_delta_time()
|
||||||
if _left_pupil:
|
if _left_pupil:
|
||||||
var target_left := Vector3(_left_base.x, _left_base.y, _left_base.z)
|
var target_left := Vector3(_left_base.x, _left_base.y, _left_base.z)
|
||||||
var pos_left := _left_pupil.position
|
var pos_left := _left_pupil.position
|
||||||
pos_left.x = move_toward(pos_left.x, target_left.x, eye_return_speed * delta)
|
pos_left.x = move_toward(pos_left.x, target_left.x, eye_return_speed * delta)
|
||||||
pos_left.y = target_left.y
|
pos_left.y = target_left.y
|
||||||
pos_left.z = move_toward(pos_left.z, target_left.z, eye_return_speed * delta)
|
pos_left.z = move_toward(pos_left.z, target_left.z, eye_return_speed * delta)
|
||||||
_left_pupil.position = pos_left
|
_left_pupil.position = pos_left
|
||||||
if _right_pupil:
|
if _right_pupil:
|
||||||
var target_right := Vector3(_right_base.x, _right_base.y, _right_base.z)
|
var target_right := Vector3(_right_base.x, _right_base.y, _right_base.z)
|
||||||
var pos_right := _right_pupil.position
|
var pos_right := _right_pupil.position
|
||||||
pos_right.x = move_toward(pos_right.x, target_right.x, eye_return_speed * delta)
|
pos_right.x = move_toward(pos_right.x, target_right.x, eye_return_speed * delta)
|
||||||
pos_right.y = target_right.y
|
pos_right.y = target_right.y
|
||||||
pos_right.z = move_toward(pos_right.z, target_right.z, eye_return_speed * delta)
|
pos_right.z = move_toward(pos_right.z, target_right.z, eye_return_speed * delta)
|
||||||
_right_pupil.position = pos_right
|
_right_pupil.position = pos_right
|
||||||
if _head:
|
if _head:
|
||||||
_head.rotation.x = _head_base_rot.x
|
_head.rotation.x = _head_base_rot.x
|
||||||
_head.rotation.y = lerp_angle(_head.rotation.y, _head_base_rot.y, head_turn_speed * delta)
|
_head.rotation.y = lerp_angle(_head.rotation.y, _head_base_rot.y, head_turn_speed * delta)
|
||||||
return
|
return
|
||||||
if lock_vertical:
|
if lock_vertical:
|
||||||
var origin_y := origin.global_position.y
|
var origin_y := origin.global_position.y
|
||||||
var target_offset := target.y - origin_y
|
var target_offset := target.y - origin_y
|
||||||
var is_above := target_offset > vertical_unlock_height
|
var is_above := target_offset > vertical_unlock_height
|
||||||
if is_above:
|
if is_above:
|
||||||
_vertical_hold_timer = vertical_lock_hold_time
|
_vertical_hold_timer = vertical_lock_hold_time
|
||||||
else:
|
else:
|
||||||
_vertical_hold_timer = max(0.0, _vertical_hold_timer - get_process_delta_time())
|
_vertical_hold_timer = max(0.0, _vertical_hold_timer - get_process_delta_time())
|
||||||
if is_above:
|
if is_above:
|
||||||
_vertical_lock_factor = 1.0
|
_vertical_lock_factor = 1.0
|
||||||
else:
|
else:
|
||||||
var unlock := 1.0 if _vertical_hold_timer > 0.0 else 0.0
|
var unlock := 1.0 if _vertical_hold_timer > 0.0 else 0.0
|
||||||
_vertical_lock_factor = move_toward(_vertical_lock_factor, unlock, vertical_lock_smooth_speed * get_process_delta_time())
|
_vertical_lock_factor = move_toward(_vertical_lock_factor, unlock, vertical_lock_smooth_speed * get_process_delta_time())
|
||||||
target.y = lerp(origin_y, target.y, _vertical_lock_factor)
|
target.y = lerp(origin_y, target.y, _vertical_lock_factor)
|
||||||
dir_world = target - origin.global_position
|
dir_world = target - origin.global_position
|
||||||
if dir_world.length_squared() <= 0.0001:
|
if dir_world.length_squared() <= 0.0001:
|
||||||
return
|
return
|
||||||
dir_world = dir_world.normalized()
|
dir_world = dir_world.normalized()
|
||||||
if _left_pupil:
|
if _left_pupil:
|
||||||
_update_eye(_left_pupil, _left_base, dir_world)
|
_update_eye(_left_pupil, _left_base, dir_world)
|
||||||
if _right_pupil:
|
if _right_pupil:
|
||||||
_update_eye(_right_pupil, _right_base, dir_world)
|
_update_eye(_right_pupil, _right_base, dir_world)
|
||||||
if _head:
|
if _head:
|
||||||
_update_head(dir_world)
|
_update_head(dir_world)
|
||||||
|
|
||||||
|
|
||||||
func _resolve_camera() -> Camera3D:
|
func _resolve_camera() -> Camera3D:
|
||||||
if camera_path != NodePath(""):
|
if camera_path != NodePath(""):
|
||||||
var from_path := get_node_or_null(camera_path) as Camera3D
|
var from_path := get_node_or_null(camera_path) as Camera3D
|
||||||
if from_path:
|
if from_path:
|
||||||
return from_path
|
return from_path
|
||||||
var viewport_cam := get_viewport().get_camera_3d()
|
var viewport_cam := get_viewport().get_camera_3d()
|
||||||
if viewport_cam:
|
if viewport_cam:
|
||||||
return viewport_cam
|
return viewport_cam
|
||||||
var by_name := get_tree().get_root().find_child("Camera3D", true, false) as Camera3D
|
var by_name := get_tree().get_root().find_child("Camera3D", true, false) as Camera3D
|
||||||
return by_name
|
return by_name
|
||||||
|
|
||||||
|
|
||||||
func _update_eye(eye: Node3D, base_pos: Vector3, dir_world: Vector3) -> void:
|
func _update_eye(eye: Node3D, base_pos: Vector3, dir_world: Vector3) -> void:
|
||||||
var parent := eye.get_parent() as Node3D
|
var parent := eye.get_parent() as Node3D
|
||||||
if parent == null:
|
if parent == null:
|
||||||
return
|
return
|
||||||
var dir_local := parent.global_basis.inverse() * dir_world
|
var dir_local := parent.global_basis.inverse() * dir_world
|
||||||
var flat := Vector2(dir_local.x, dir_local.y)
|
var flat := Vector2(dir_local.x, dir_local.y)
|
||||||
flat.x *= side_eye_boost
|
flat.x *= side_eye_boost
|
||||||
if flat.length() > 1.0:
|
if flat.length() > 1.0:
|
||||||
flat = flat.normalized()
|
flat = flat.normalized()
|
||||||
var offset := Vector3(flat.x, flat.y, 0.0) * max_offset
|
var offset := Vector3(flat.x, flat.y, 0.0) * max_offset
|
||||||
eye.position = base_pos + offset
|
eye.position = base_pos + offset
|
||||||
|
|
||||||
|
|
||||||
func _update_head(dir_world: Vector3) -> void:
|
func _update_head(dir_world: Vector3) -> void:
|
||||||
var parent := _head.get_parent() as Node3D
|
var parent := _head.get_parent() as Node3D
|
||||||
if parent == null:
|
if parent == null:
|
||||||
return
|
return
|
||||||
var dir_local := parent.global_basis.inverse() * dir_world
|
var dir_local := parent.global_basis.inverse() * dir_world
|
||||||
var yaw := atan2(-dir_local.x, -dir_local.z)
|
var yaw := atan2(-dir_local.x, -dir_local.z)
|
||||||
var pitch := atan2(dir_local.y, -dir_local.z)
|
var pitch := atan2(dir_local.y, -dir_local.z)
|
||||||
yaw = clamp(yaw, deg_to_rad(-head_max_yaw_deg), deg_to_rad(head_max_yaw_deg))
|
yaw = clamp(yaw, deg_to_rad(-head_max_yaw_deg), deg_to_rad(head_max_yaw_deg))
|
||||||
pitch = clamp(pitch, deg_to_rad(-head_max_pitch_deg), deg_to_rad(head_max_pitch_deg))
|
pitch = clamp(pitch, deg_to_rad(-head_max_pitch_deg), deg_to_rad(head_max_pitch_deg))
|
||||||
var target := Vector3(_head_base_rot.x + pitch, _head_base_rot.y + yaw, _head_base_rot.z)
|
var target := Vector3(_head_base_rot.x + pitch, _head_base_rot.y + yaw, _head_base_rot.z)
|
||||||
_head.rotation.x = lerp_angle(_head.rotation.x, target.x, head_turn_speed * get_process_delta_time())
|
_head.rotation.x = lerp_angle(_head.rotation.x, target.x, head_turn_speed * get_process_delta_time())
|
||||||
_head.rotation.y = lerp_angle(_head.rotation.y, target.y, head_turn_speed * get_process_delta_time())
|
_head.rotation.y = lerp_angle(_head.rotation.y, target.y, head_turn_speed * get_process_delta_time())
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bs3eqqujhetsm
|
uid://bs3eqqujhetsm
|
||||||
|
|||||||
@@ -1,111 +1,116 @@
|
|||||||
[gd_scene load_steps=14 format=3]
|
[gd_scene load_steps=14 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scenes/Characters/repo_bot.gd" id="1_repo_bot"]
|
[ext_resource type="Script" path="res://scenes/Characters/repo_bot.gd" id="1_repo_bot"]
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_body"]
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_body"]
|
||||||
albedo_color = Color(0.78, 0.8, 0.82, 1)
|
albedo_color = Color(0.78, 0.8, 0.82, 1)
|
||||||
metallic = 0.2
|
metallic = 0.2
|
||||||
roughness = 0.35
|
roughness = 0.35
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_accent"]
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_accent"]
|
||||||
albedo_color = Color(0.25, 0.82, 0.55, 1)
|
albedo_color = Color(0.25, 0.82, 0.55, 1)
|
||||||
emission_enabled = true
|
emission_enabled = true
|
||||||
emission = Color(0.25, 0.82, 0.55, 1)
|
emission = Color(0.25, 0.82, 0.55, 1)
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eye_white"]
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_eye_white"]
|
||||||
albedo_color = Color(0.95, 0.95, 0.95, 1)
|
albedo_color = Color(0.95, 0.95, 0.95, 1)
|
||||||
roughness = 0.2
|
roughness = 0.2
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pupil"]
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_pupil"]
|
||||||
albedo_color = Color(0.02, 0.02, 0.02, 1)
|
albedo_color = Color(0.02, 0.02, 0.02, 1)
|
||||||
roughness = 0.8
|
roughness = 0.8
|
||||||
|
|
||||||
[sub_resource type="CapsuleMesh" id="CapsuleMesh_body"]
|
[sub_resource type="CapsuleMesh" id="CapsuleMesh_body"]
|
||||||
radius = 0.25
|
radius = 0.25
|
||||||
height = 0.6
|
height = 0.6
|
||||||
material = SubResource("StandardMaterial3D_body")
|
material = SubResource("StandardMaterial3D_body")
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="SphereMesh_head"]
|
[sub_resource type="SphereMesh" id="SphereMesh_head"]
|
||||||
radius = 0.22
|
radius = 0.22
|
||||||
height = 0.44
|
height = 0.44
|
||||||
material = SubResource("StandardMaterial3D_body")
|
material = SubResource("StandardMaterial3D_body")
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="SphereMesh_eye_white"]
|
[sub_resource type="SphereMesh" id="SphereMesh_eye_white"]
|
||||||
radius = 0.075
|
radius = 0.075
|
||||||
height = 0.15
|
height = 0.15
|
||||||
material = SubResource("StandardMaterial3D_eye_white")
|
material = SubResource("StandardMaterial3D_eye_white")
|
||||||
|
|
||||||
[sub_resource type="SphereMesh" id="SphereMesh_pupil"]
|
[sub_resource type="SphereMesh" id="SphereMesh_pupil"]
|
||||||
radius = 0.028
|
radius = 0.028
|
||||||
height = 0.056
|
height = 0.056
|
||||||
material = SubResource("StandardMaterial3D_pupil")
|
material = SubResource("StandardMaterial3D_pupil")
|
||||||
|
|
||||||
[sub_resource type="CylinderMesh" id="CylinderMesh_limb"]
|
[sub_resource type="CylinderMesh" id="CylinderMesh_limb"]
|
||||||
top_radius = 0.06
|
top_radius = 0.06
|
||||||
bottom_radius = 0.06
|
bottom_radius = 0.06
|
||||||
height = 0.35
|
height = 0.35
|
||||||
material = SubResource("StandardMaterial3D_body")
|
material = SubResource("StandardMaterial3D_body")
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_pack"]
|
[sub_resource type="BoxMesh" id="BoxMesh_pack"]
|
||||||
size = Vector3(0.26, 0.3, 0.12)
|
size = Vector3(0.26, 0.3, 0.12)
|
||||||
material = SubResource("StandardMaterial3D_accent")
|
material = SubResource("StandardMaterial3D_accent")
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
|
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_body"]
|
||||||
radius = 0.3
|
radius = 0.3
|
||||||
height = 1.1
|
height = 1.1
|
||||||
|
|
||||||
[node name="RepoBot" type="Node3D"]
|
[node name="RepoBot" type="Node3D"]
|
||||||
script = ExtResource("1_repo_bot")
|
script = ExtResource("1_repo_bot")
|
||||||
|
|
||||||
[node name="Body" type="StaticBody3D" parent="."]
|
[node name="Body" type="RigidBody3D" parent="."]
|
||||||
|
mass = 1.5
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
|
axis_lock_angular_x = true
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0)
|
axis_lock_angular_z = true
|
||||||
shape = SubResource("CapsuleShape3D_body")
|
angular_damp = 8.0
|
||||||
|
linear_damp = 0.5
|
||||||
[node name="Torso" type="MeshInstance3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Body"]
|
||||||
mesh = SubResource("CapsuleMesh_body")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.55, 0)
|
||||||
|
shape = SubResource("CapsuleShape3D_body")
|
||||||
[node name="HeadPivot" type="Node3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0)
|
[node name="Torso" type="MeshInstance3D" parent="Body"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||||
[node name="Head" type="MeshInstance3D" parent="Body/HeadPivot"]
|
mesh = SubResource("CapsuleMesh_body")
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
|
||||||
mesh = SubResource("SphereMesh_head")
|
[node name="HeadPivot" type="Node3D" parent="Body"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.95, 0)
|
||||||
[node name="EyeLeft" type="MeshInstance3D" parent="Body/HeadPivot"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.09, 0, -0.205)
|
[node name="Head" type="MeshInstance3D" parent="Body/HeadPivot"]
|
||||||
mesh = SubResource("SphereMesh_eye_white")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0)
|
||||||
|
mesh = SubResource("SphereMesh_head")
|
||||||
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeLeft"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
|
[node name="EyeLeft" type="MeshInstance3D" parent="Body/HeadPivot"]
|
||||||
mesh = SubResource("SphereMesh_pupil")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.09, 0, -0.205)
|
||||||
|
mesh = SubResource("SphereMesh_eye_white")
|
||||||
[node name="EyeRight" type="MeshInstance3D" parent="Body/HeadPivot"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.09, 0, -0.205)
|
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeLeft"]
|
||||||
mesh = SubResource("SphereMesh_eye_white")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
|
||||||
|
mesh = SubResource("SphereMesh_pupil")
|
||||||
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeRight"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
|
[node name="EyeRight" type="MeshInstance3D" parent="Body/HeadPivot"]
|
||||||
mesh = SubResource("SphereMesh_pupil")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.09, 0, -0.205)
|
||||||
|
mesh = SubResource("SphereMesh_eye_white")
|
||||||
[node name="ArmLeft" type="MeshInstance3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.32, 0.55, 0)
|
[node name="Pupil" type="MeshInstance3D" parent="Body/HeadPivot/EyeRight"]
|
||||||
mesh = SubResource("CylinderMesh_limb")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.06)
|
||||||
|
mesh = SubResource("SphereMesh_pupil")
|
||||||
[node name="ArmRight" type="MeshInstance3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.32, 0.55, 0)
|
[node name="ArmLeft" type="MeshInstance3D" parent="Body"]
|
||||||
mesh = SubResource("CylinderMesh_limb")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.32, 0.55, 0)
|
||||||
|
mesh = SubResource("CylinderMesh_limb")
|
||||||
[node name="LegLeft" type="MeshInstance3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.12, 0.15, 0)
|
[node name="ArmRight" type="MeshInstance3D" parent="Body"]
|
||||||
mesh = SubResource("CylinderMesh_limb")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.32, 0.55, 0)
|
||||||
|
mesh = SubResource("CylinderMesh_limb")
|
||||||
[node name="LegRight" type="MeshInstance3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.15, 0)
|
[node name="LegLeft" type="MeshInstance3D" parent="Body"]
|
||||||
mesh = SubResource("CylinderMesh_limb")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.12, 0.15, 0)
|
||||||
|
mesh = SubResource("CylinderMesh_limb")
|
||||||
[node name="Backpack" type="MeshInstance3D" parent="Body"]
|
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0.22)
|
[node name="LegRight" type="MeshInstance3D" parent="Body"]
|
||||||
mesh = SubResource("BoxMesh_pack")
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.12, 0.15, 0)
|
||||||
|
mesh = SubResource("CylinderMesh_limb")
|
||||||
|
|
||||||
|
[node name="Backpack" type="MeshInstance3D" parent="Body"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.6, 0.22)
|
||||||
|
mesh = SubResource("BoxMesh_pack")
|
||||||
|
|||||||
+22
-22
@@ -1,22 +1,22 @@
|
|||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
@export var day_length := 120.0 # seconds for full rotation
|
@export var day_length := 120.0 # seconds for full rotation
|
||||||
@export var start_light_angle := -90.0
|
@export var start_light_angle := -90.0
|
||||||
var end_light_angle = start_light_angle + 360.0
|
var end_light_angle = start_light_angle + 360.0
|
||||||
var start_radians = start_light_angle * PI / 180
|
var start_radians = start_light_angle * PI / 180
|
||||||
var time := 0.0
|
var time := 0.0
|
||||||
|
|
||||||
@onready var sun := $DirectionalLight3D
|
@onready var sun := $DirectionalLight3D
|
||||||
|
|
||||||
func _process(delta):
|
func _process(delta):
|
||||||
time = fmod((time + delta), day_length)
|
time = fmod((time + delta), day_length)
|
||||||
var t = time / day_length
|
var t = time / day_length
|
||||||
|
|
||||||
# Rotate sun around X axis
|
# Rotate sun around X axis
|
||||||
var angle = lerp(start_light_angle, end_light_angle, t) # sunrise → sunset → night → sunrise
|
var angle = lerp(start_light_angle, end_light_angle, t) # sunrise → sunset → night → sunrise
|
||||||
sun.rotation_degrees.x = angle
|
sun.rotation_degrees.x = angle
|
||||||
|
|
||||||
# Adjust intensity
|
# Adjust intensity
|
||||||
var curSin = -sin((t * TAU) + start_radians)
|
var curSin = -sin((t * TAU) + start_radians)
|
||||||
var energy = clamp((curSin * 1.0) + 0.2, 0.0, 1.2)
|
var energy = clamp((curSin * 1.0) + 0.2, 0.0, 1.2)
|
||||||
sun.light_energy = energy
|
sun.light_energy = energy
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://brgmxhhhtakja
|
uid://brgmxhhhtakja
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
[gd_scene load_steps=17 format=3 uid="uid://dchj6g2i8ebph"]
|
[gd_scene load_steps=18 format=3 uid="uid://dchj6g2i8ebph"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://brgmxhhhtakja" path="res://scenes/Levels/level.gd" id="1_a4mo8"]
|
[ext_resource type="Script" uid="uid://brgmxhhhtakja" path="res://scenes/Levels/level.gd" id="1_a4mo8"]
|
||||||
[ext_resource type="PackedScene" uid="uid://bb6hj6l23043x" path="res://assets/models/human.blend" id="1_eg4yq"]
|
[ext_resource type="PackedScene" uid="uid://bb6hj6l23043x" path="res://assets/models/human.blend" id="1_eg4yq"]
|
||||||
@@ -6,6 +6,8 @@
|
|||||||
[ext_resource type="PackedScene" uid="uid://c5of6aaxop1hl" path="res://scenes/block.tscn" id="2_tc7dm"]
|
[ext_resource type="PackedScene" uid="uid://c5of6aaxop1hl" path="res://scenes/block.tscn" id="2_tc7dm"]
|
||||||
[ext_resource type="Script" uid="uid://b7fopt7sx74g8" path="res://scenes/Levels/menu.gd" id="3_tc7dm"]
|
[ext_resource type="Script" uid="uid://b7fopt7sx74g8" path="res://scenes/Levels/menu.gd" id="3_tc7dm"]
|
||||||
[ext_resource type="PackedScene" path="res://scenes/Characters/repo_bot.tscn" id="4_repo"]
|
[ext_resource type="PackedScene" path="res://scenes/Characters/repo_bot.tscn" id="4_repo"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://bnqaqbgynoyys" path="res://assets/models/TestCharAnimated.glb" id="5_fi66n"]
|
||||||
|
[ext_resource type="PackedScene" path="res://scenes/Vehicles/car.tscn" id="5_car"]
|
||||||
|
|
||||||
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_2q6dc"]
|
[sub_resource type="PhysicsMaterial" id="PhysicsMaterial_2q6dc"]
|
||||||
bounce = 0.5
|
bounce = 0.5
|
||||||
@@ -42,6 +44,7 @@ script = ExtResource("1_a4mo8")
|
|||||||
|
|
||||||
[node name="RepoBot" parent="." instance=ExtResource("4_repo")]
|
[node name="RepoBot" parent="." instance=ExtResource("4_repo")]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9426608, 0, -4.4451966)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.9426608, 0, -4.4451966)
|
||||||
|
look_target_path = NodePath("../Player")
|
||||||
|
|
||||||
[node name="Thing" type="RigidBody3D" parent="."]
|
[node name="Thing" type="RigidBody3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -3.7986288)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -3.7986288)
|
||||||
@@ -62,16 +65,142 @@ mesh = SubResource("SphereMesh_w7c3h")
|
|||||||
physics_material_override = SubResource("PhysicsMaterial_w8frs")
|
physics_material_override = SubResource("PhysicsMaterial_w8frs")
|
||||||
script = ExtResource("1_muv8p")
|
script = ExtResource("1_muv8p")
|
||||||
camera_path = NodePath("Camera3D")
|
camera_path = NodePath("Camera3D")
|
||||||
|
phone_path = NodePath("../PhoneUI")
|
||||||
|
|
||||||
|
[node name="TestCharAnimated" parent="Player" instance=ExtResource("5_fi66n")]
|
||||||
|
transform = Transform3D(-0.9998549, 0, 0.01703362, 0, 1, 0, -0.01703362, 0, -0.9998549, 0, 0, 0)
|
||||||
|
|
||||||
|
[node name="Skeleton3D" parent="Player/TestCharAnimated/Armature" index="0"]
|
||||||
|
bones/0/position = Vector3(0.0026170756, -0.0012418391, -0.98996276)
|
||||||
|
bones/0/rotation = Quaternion(-0.7278173, -0.05346041, 0.012412205, 0.6835714)
|
||||||
|
bones/1/position = Vector3(8.593209e-06, 0.09923458, -0.012273359)
|
||||||
|
bones/1/rotation = Quaternion(-0.031655744, 0.011362745, 0.022744056, 0.9991754)
|
||||||
|
bones/2/position = Vector3(-2.2989244e-10, 0.117319785, 2.9730852e-08)
|
||||||
|
bones/2/rotation = Quaternion(0.010291794, 0.001169461, 0.013815534, 0.9998509)
|
||||||
|
bones/3/position = Vector3(-2.3358266e-09, 0.13458829, -1.382244e-08)
|
||||||
|
bones/3/rotation = Quaternion(0.067557335, 0.0021622085, 0.013776206, 0.99761796)
|
||||||
|
bones/4/position = Vector3(2.508626e-07, 0.15027755, 0.008779066)
|
||||||
|
bones/4/rotation = Quaternion(0.051882792, -0.00090027065, 0.0039363643, 0.998645)
|
||||||
|
bones/5/position = Vector3(2.152588e-08, 0.103218146, 0.03142428)
|
||||||
|
bones/5/rotation = Quaternion(-0.06595954, 0.011917545, -0.0014885183, 0.99775004)
|
||||||
|
bones/6/position = Vector3(1.5477327e-06, 0.18474667, 0.06636399)
|
||||||
|
bones/7/position = Vector3(0.061058242, 0.09106279, 0.0075706206)
|
||||||
|
bones/7/rotation = Quaternion(-0.5982132, -0.43452233, 0.50765175, -0.4422909)
|
||||||
|
bones/8/position = Vector3(1.1842036e-08, 0.12922287, 5.2578955e-08)
|
||||||
|
bones/8/rotation = Quaternion(0.5434686, 0.18407291, 0.20301422, 0.7934383)
|
||||||
|
bones/9/position = Vector3(1.5054144e-07, 0.2740467, 2.2048344e-08)
|
||||||
|
bones/9/rotation = Quaternion(-5.192343e-09, -1.1239315e-06, 0.01273731, 0.99991894)
|
||||||
|
bones/10/position = Vector3(1.9432857e-08, 0.27614468, 9.6609995e-08)
|
||||||
|
bones/10/rotation = Quaternion(0.048323337, -0.28487536, 0.026756868, 0.9569718)
|
||||||
|
bones/11/position = Vector3(-0.030029751, 0.037888147, 0.021671427)
|
||||||
|
bones/11/rotation = Quaternion(0.2098604, -0.059559863, 0.20750383, 0.9536002)
|
||||||
|
bones/12/position = Vector3(-3.3527822e-08, 0.0474497, -1.48403245e-08)
|
||||||
|
bones/12/rotation = Quaternion(-0.14269812, 0.08661232, -0.24053419, 0.9561793)
|
||||||
|
bones/13/position = Vector3(-9.313215e-09, 0.043821093, 1.7813335e-08)
|
||||||
|
bones/13/rotation = Quaternion(-0.121022485, -0.07554186, 0.024044419, 0.98947906)
|
||||||
|
bones/14/position = Vector3(-4.847594e-08, 0.034590863, -5.3379924e-08)
|
||||||
|
bones/15/position = Vector3(-0.02822038, 0.12266627, 0.0023182186)
|
||||||
|
bones/15/rotation = Quaternion(0.06621808, 0.00073213095, 0.008435749, 0.99776924)
|
||||||
|
bones/16/position = Vector3(-8.136956e-09, 0.03891963, -1.4837047e-08)
|
||||||
|
bones/16/rotation = Quaternion(0.1509963, 7.805282e-07, -0.015479834, 0.98841316)
|
||||||
|
bones/17/position = Vector3(7.499967e-09, 0.034151517, -5.4443618e-08)
|
||||||
|
bones/17/rotation = Quaternion(0.070082076, 7.71772e-07, -0.007181638, 0.99751544)
|
||||||
|
bones/18/position = Vector3(2.700483e-09, 0.030779874, 4.9589467e-08)
|
||||||
|
bones/19/position = Vector3(-1.2452401e-07, 0.12775533, -8.01722e-08)
|
||||||
|
bones/19/rotation = Quaternion(0.13653132, -0.0038339633, -0.022952663, 0.9903624)
|
||||||
|
bones/20/position = Vector3(2.5990463e-09, 0.036139663, 1.713758e-10)
|
||||||
|
bones/20/rotation = Quaternion(0.15125485, 1.1823237e-06, -0.015500045, 0.9883733)
|
||||||
|
bones/21/position = Vector3(2.9034163e-07, 0.034597658, -3.6051716e-08)
|
||||||
|
bones/21/rotation = Quaternion(0.1599333, 1.4124467e-06, -0.016389191, 0.98699176)
|
||||||
|
bones/22/position = Vector3(-3.0519334e-07, 0.03680197, -9.1170705e-08)
|
||||||
|
bones/23/position = Vector3(0.022166358, 0.12147022, -9.993157e-05)
|
||||||
|
bones/23/rotation = Quaternion(0.18325116, -0.004651328, -0.031451236, 0.9825519)
|
||||||
|
bones/24/position = Vector3(4.5585904e-09, 0.036011815, -8.07135e-09)
|
||||||
|
bones/24/rotation = Quaternion(0.18138836, 1.5675009e-06, -0.018595627, 0.9832358)
|
||||||
|
bones/25/position = Vector3(-9.647604e-09, 0.033073086, -1.46341534e-08)
|
||||||
|
bones/25/rotation = Quaternion(0.1552087, 2.2643005e-06, -0.015905593, 0.9877537)
|
||||||
|
bones/26/position = Vector3(-2.434721e-07, 0.03660111, 2.471306e-07)
|
||||||
|
bones/27/position = Vector3(0.04725836, 0.109081924, 0.0022613404)
|
||||||
|
bones/27/rotation = Quaternion(0.23387046, -0.0035344863, -0.048872076, 0.97103226)
|
||||||
|
bones/28/position = Vector3(4.3135207e-09, 0.041366525, 3.569472e-08)
|
||||||
|
bones/28/rotation = Quaternion(0.15124865, 1.4340599e-05, -0.015863812, 0.98836845)
|
||||||
|
bones/29/position = Vector3(1.195453e-08, 0.02594832, 1.5542597e-08)
|
||||||
|
bones/29/rotation = Quaternion(0.14350323, 3.322942e-05, -0.014478063, 0.98954403)
|
||||||
|
bones/30/position = Vector3(-2.4944999e-08, 0.029238665, 6.2325825e-08)
|
||||||
|
bones/31/position = Vector3(-0.06105696, 0.09106397, 0.007570758)
|
||||||
|
bones/31/rotation = Quaternion(0.60009927, -0.4346891, 0.52418536, 0.4197095)
|
||||||
|
bones/32/position = Vector3(2.116817e-08, 0.12922288, 7.016769e-08)
|
||||||
|
bones/32/rotation = Quaternion(0.46432823, -0.26300937, -0.23269261, 0.813068)
|
||||||
|
bones/33/position = Vector3(-2.350565e-08, 0.27404687, 1.5161348e-08)
|
||||||
|
bones/33/rotation = Quaternion(7.667138e-08, -5.0620065e-06, -0.058376648, 0.99829465)
|
||||||
|
bones/34/position = Vector3(1.3629875e-07, 0.27614468, -9.158248e-09)
|
||||||
|
bones/34/rotation = Quaternion(0.059040155, 0.17509013, -0.005968382, 0.9827626)
|
||||||
|
bones/35/position = Vector3(0.030029776, 0.03788806, 0.021671649)
|
||||||
|
bones/35/rotation = Quaternion(0.21378386, 0.065372065, -0.22564878, 0.94821185)
|
||||||
|
bones/36/position = Vector3(-2.7939748e-09, 0.04744962, -1.7240383e-08)
|
||||||
|
bones/36/rotation = Quaternion(-0.14410053, -0.076146945, 0.2772088, 0.9468854)
|
||||||
|
bones/37/position = Vector3(5.58785e-09, 0.04382136, 3.566345e-08)
|
||||||
|
bones/37/rotation = Quaternion(-0.1552004, 0.07467698, -0.026057797, 0.9847117)
|
||||||
|
bones/38/position = Vector3(8.2187e-08, 0.034590647, -2.4929412e-07)
|
||||||
|
bones/39/position = Vector3(0.028220426, 0.1226661, 0.0023184721)
|
||||||
|
bones/39/rotation = Quaternion(0.059513006, -0.0009918809, -0.010980465, 0.99816674)
|
||||||
|
bones/40/position = Vector3(-1.1795999e-08, 0.038919587, -4.9162594e-09)
|
||||||
|
bones/40/rotation = Quaternion(0.15128906, 2.1640317e-06, 0.012328284, 0.98841274)
|
||||||
|
bones/41/position = Vector3(-1.4053852e-07, 0.034151636, -1.1118345e-08)
|
||||||
|
bones/41/rotation = Quaternion(0.07021882, 5.966413e-07, 0.0057199914, 0.9975152)
|
||||||
|
bones/42/position = Vector3(-7.1266735e-09, 0.030779934, 3.7720538e-10)
|
||||||
|
bones/43/position = Vector3(1.69263e-07, 0.12775521, 2.5128352e-07)
|
||||||
|
bones/43/rotation = Quaternion(0.13051407, 0.003691554, 0.018921439, 0.99125904)
|
||||||
|
bones/44/position = Vector3(4.8907918e-09, 0.036139727, -8.499214e-09)
|
||||||
|
bones/44/rotation = Quaternion(0.15154669, 1.6962191e-06, 0.012345144, 0.988373)
|
||||||
|
bones/45/position = Vector3(-2.8403383e-07, 0.03459763, -4.4995545e-09)
|
||||||
|
bones/45/rotation = Quaternion(0.16023898, 1.233635e-06, 0.013053549, 0.98699194)
|
||||||
|
bones/46/position = Vector3(2.8485744e-07, 0.03680191, -9.493007e-09)
|
||||||
|
bones/47/position = Vector3(-0.022166327, 0.12146996, -9.982591e-05)
|
||||||
|
bones/47/rotation = Quaternion(0.1774532, 0.0045050457, 0.02645378, 0.98376334)
|
||||||
|
bones/48/position = Vector3(4.6136024e-09, 0.036011953, 3.056637e-08)
|
||||||
|
bones/48/rotation = Quaternion(0.18173504, 1.8501213e-06, 0.014812392, 0.98323596)
|
||||||
|
bones/49/position = Vector3(-3.407626e-08, 0.033073153, 1.8664657e-08)
|
||||||
|
bones/49/rotation = Quaternion(0.15551038, 3.26233e-07, 0.012668259, 0.98775303)
|
||||||
|
bones/50/position = Vector3(2.3113911e-07, 0.03660121, -1.1142021e-08)
|
||||||
|
bones/51/position = Vector3(-0.047258336, 0.109081805, 0.0022614468)
|
||||||
|
bones/51/rotation = Quaternion(0.22849467, 0.003407588, 0.042977322, 0.97259015)
|
||||||
|
bones/52/position = Vector3(-1.5261804e-08, 0.041366428, -1.9607107e-09)
|
||||||
|
bones/52/rotation = Quaternion(0.1515478, 7.4422354e-07, 0.01235583, 0.98837274)
|
||||||
|
bones/53/position = Vector3(1.4340932e-08, 0.025948457, 3.3946527e-08)
|
||||||
|
bones/53/rotation = Quaternion(0.14377311, 5.8452275e-07, 0.011701506, 0.98954153)
|
||||||
|
bones/54/position = Vector3(4.750415e-09, 0.029238641, -1.2388978e-09)
|
||||||
|
bones/55/position = Vector3(0.09123873, -0.06657194, -0.0005540352)
|
||||||
|
bones/55/rotation = Quaternion(0.13601132, 0.07838244, 0.9844897, -0.07834015)
|
||||||
|
bones/56/position = Vector3(-1.3288446e-09, 0.40599436, 1.0443015e-08)
|
||||||
|
bones/56/rotation = Quaternion(-0.13252214, -0.014519276, 0.03215593, 0.99055195)
|
||||||
|
bones/57/position = Vector3(5.707578e-09, 0.42099008, -1.5425995e-08)
|
||||||
|
bones/57/rotation = Quaternion(0.5344839, -0.021701135, -0.027458949, 0.8444537)
|
||||||
|
bones/58/position = Vector3(-6.5650525e-09, 0.15721555, -2.6694579e-08)
|
||||||
|
bones/58/rotation = Quaternion(0.2693438, -0.032380052, -0.014592491, 0.96238893)
|
||||||
|
bones/59/position = Vector3(2.4854705e-08, 0.09999996, -3.1755625e-09)
|
||||||
|
bones/60/position = Vector3(-0.091250315, -0.066556, -0.0005535231)
|
||||||
|
bones/60/rotation = Quaternion(-0.09026457, -0.016758345, 0.9954146, -0.026857648)
|
||||||
|
bones/61/position = Vector3(1.3129254e-08, 0.40599442, 3.8557886e-09)
|
||||||
|
bones/61/rotation = Quaternion(-0.07401098, 0.084030755, -0.024547135, 0.99340767)
|
||||||
|
bones/62/position = Vector3(-7.3885964e-10, 0.42099023, -5.226429e-09)
|
||||||
|
bones/62/rotation = Quaternion(0.56540585, 0.011310213, -0.008501466, 0.8246915)
|
||||||
|
bones/63/position = Vector3(7.93632e-09, 0.1572156, -2.6982683e-10)
|
||||||
|
bones/63/rotation = Quaternion(0.28907102, 0.031904068, 0.014082117, 0.9566723)
|
||||||
|
bones/64/position = Vector3(7.567915e-10, 0.099999994, -3.2595668e-09)
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="Player"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.5, 0)
|
||||||
shape = SubResource("SphereShape3D_mx8sn")
|
shape = SubResource("SphereShape3D_mx8sn")
|
||||||
|
|
||||||
[node name="Camera3D" type="Camera3D" parent="Player"]
|
[node name="Camera3D" type="Camera3D" parent="Player"]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.31670225, 0)
|
transform = Transform3D(0.9989785, -4.651856e-10, -0.045188628, 0.006969331, 0.9880354, 0.15407, 0.044647958, -0.15422754, 0.9870261, 0.22036135, 1.8988357, 0.64972365)
|
||||||
current = true
|
current = true
|
||||||
|
fov = 49.0
|
||||||
|
|
||||||
[node name="SpotLight3D" type="SpotLight3D" parent="Player"]
|
[node name="SpotLight3D" type="SpotLight3D" parent="Player"]
|
||||||
|
|
||||||
|
[node name="Car" parent="." instance=ExtResource("5_car")]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -6, 0, -3)
|
||||||
[node name="Ground" type="StaticBody3D" parent="."]
|
[node name="Ground" type="StaticBody3D" parent="."]
|
||||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
|
||||||
|
|
||||||
@@ -79,6 +208,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
|
|||||||
shape = SubResource("BoxShape3D_2q6dc")
|
shape = SubResource("BoxShape3D_2q6dc")
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="Ground"]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.00053596497, 0.0075991154, -0.0019865036)
|
||||||
mesh = SubResource("BoxMesh_w7c3h")
|
mesh = SubResource("BoxMesh_w7c3h")
|
||||||
|
|
||||||
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
@@ -134,9 +264,38 @@ text = "Main Menu"
|
|||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Quit"
|
text = "Quit"
|
||||||
|
|
||||||
|
[node name="PhoneUI" type="CanvasLayer" parent="."]
|
||||||
|
layer = 5
|
||||||
|
visible = false
|
||||||
|
|
||||||
|
[node name="Control" type="Control" parent="PhoneUI"]
|
||||||
|
layout_mode = 3
|
||||||
|
anchors_preset = 15
|
||||||
|
anchor_right = 1.0
|
||||||
|
anchor_bottom = 1.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="PhoneFrame" type="ColorRect" parent="PhoneUI/Control"]
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -180.0
|
||||||
|
offset_top = -320.0
|
||||||
|
offset_right = 180.0
|
||||||
|
offset_bottom = 320.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
color = Color(0.08, 0.08, 0.1, 1)
|
||||||
|
|
||||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||||
environment = SubResource("Environment_a4mo8")
|
environment = SubResource("Environment_a4mo8")
|
||||||
|
|
||||||
[connection signal="pressed" from="Menu/Control/VBoxContainer/ContinueButton" to="Menu" method="_on_continue_button_pressed"]
|
[connection signal="pressed" from="Menu/Control/VBoxContainer/ContinueButton" to="Menu" method="_on_continue_button_pressed"]
|
||||||
[connection signal="pressed" from="Menu/Control/VBoxContainer/MainMenuButton" to="Menu" method="_on_main_menu_button_pressed"]
|
[connection signal="pressed" from="Menu/Control/VBoxContainer/MainMenuButton" to="Menu" method="_on_main_menu_button_pressed"]
|
||||||
[connection signal="pressed" from="Menu/Control/VBoxContainer/QuitButton" to="Menu" method="_on_quit_button_pressed"]
|
[connection signal="pressed" from="Menu/Control/VBoxContainer/QuitButton" to="Menu" method="_on_quit_button_pressed"]
|
||||||
|
|
||||||
|
[editable path="Player/TestCharAnimated"]
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
extends Node3D
|
||||||
|
|
||||||
|
@export var tile_size := 4.0
|
||||||
|
@export var block_height := 1.0
|
||||||
|
|
||||||
|
@onready var _block: MeshInstance3D = $TerrainBlock
|
||||||
|
@onready var _camera: Camera3D = $Camera3D
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
var coord := SelectedCharacter.get_coord()
|
||||||
|
var block_pos := Vector3(coord.x * tile_size, block_height * 0.5, coord.y * tile_size)
|
||||||
|
_block.position = block_pos
|
||||||
|
_block.scale = Vector3(tile_size, block_height, tile_size)
|
||||||
|
if _camera:
|
||||||
|
_camera.look_at(block_pos, Vector3.UP)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://1fico5npv6dy
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
[gd_scene load_steps=4 format=3 uid="uid://b7p7k1i4t0m2l"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scenes/Levels/location_level.gd" id="1_6y4q1"]
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yu2x4"]
|
||||||
|
albedo_color = Color(0.2, 0.6, 0.2, 1)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_t2a5k"]
|
||||||
|
material = SubResource("StandardMaterial3D_yu2x4")
|
||||||
|
|
||||||
|
[node name="LocationLevel" type="Node3D"]
|
||||||
|
script = ExtResource("1_6y4q1")
|
||||||
|
|
||||||
|
[node name="TerrainBlock" type="MeshInstance3D" parent="."]
|
||||||
|
mesh = SubResource("BoxMesh_t2a5k")
|
||||||
|
|
||||||
|
[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.819152, 0.573576, 0, -0.573576, 0.819152, 0, 6, 0)
|
||||||
|
shadow_enabled = true
|
||||||
|
|
||||||
|
[node name="Camera3D" type="Camera3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 0.92388, 0.382683, 0, -0.382683, 0.92388, 0, 6, 10)
|
||||||
|
current = true
|
||||||
+45
-42
@@ -1,51 +1,54 @@
|
|||||||
extends CanvasLayer
|
extends CanvasLayer
|
||||||
|
|
||||||
const START_SCREEN_SCENE := "res://scenes/UI/start_screen.tscn"
|
const START_SCREEN_SCENE := "res://scenes/UI/start_screen.tscn"
|
||||||
|
|
||||||
@onready var pause_menu = $Control
|
@onready var pause_menu = $Control
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_register_focus_sounds()
|
_register_focus_sounds()
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event.is_action_pressed("ui_cancel"):
|
if event.is_action_pressed("ui_cancel"):
|
||||||
if get_tree().paused:
|
if get_tree().paused:
|
||||||
resume_game()
|
resume_game()
|
||||||
else:
|
else:
|
||||||
pause_game()
|
pause_game()
|
||||||
|
|
||||||
func pause_game():
|
func pause_game():
|
||||||
get_tree().paused = true
|
get_tree().paused = true
|
||||||
visible = true
|
visible = true
|
||||||
|
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||||
|
|
||||||
func resume_game():
|
func resume_game():
|
||||||
get_tree().paused = false
|
get_tree().paused = false
|
||||||
visible = false
|
visible = false
|
||||||
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||||
func _on_quit_button_pressed():
|
|
||||||
get_tree().quit()
|
func _on_quit_button_pressed():
|
||||||
|
get_tree().quit()
|
||||||
func _on_continue_button_pressed():
|
|
||||||
resume_game()
|
func _on_continue_button_pressed():
|
||||||
|
resume_game()
|
||||||
|
|
||||||
func _on_main_menu_button_pressed():
|
func _on_main_menu_button_pressed():
|
||||||
resume_game()
|
resume_game()
|
||||||
|
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||||
get_tree().change_scene_to_file(START_SCREEN_SCENE)
|
get_tree().change_scene_to_file(START_SCREEN_SCENE)
|
||||||
|
|
||||||
func _register_focus_sounds() -> void:
|
func _register_focus_sounds() -> void:
|
||||||
if pause_menu == null:
|
if pause_menu == null:
|
||||||
return
|
return
|
||||||
var vbox := pause_menu.get_node_or_null("VBoxContainer")
|
var vbox := pause_menu.get_node_or_null("VBoxContainer")
|
||||||
if vbox == null:
|
if vbox == null:
|
||||||
return
|
return
|
||||||
for child in vbox.get_children():
|
for child in vbox.get_children():
|
||||||
if child is BaseButton:
|
if child is BaseButton:
|
||||||
var button: BaseButton = child
|
var button: BaseButton = child
|
||||||
if not button.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")):
|
if not button.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")):
|
||||||
button.focus_entered.connect(_on_menu_item_focus)
|
button.focus_entered.connect(_on_menu_item_focus)
|
||||||
if not button.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")):
|
if not button.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")):
|
||||||
button.mouse_entered.connect(_on_menu_item_focus)
|
button.mouse_entered.connect(_on_menu_item_focus)
|
||||||
|
|
||||||
func _on_menu_item_focus() -> void:
|
func _on_menu_item_focus() -> void:
|
||||||
if MenuSfx:
|
if MenuSfx:
|
||||||
MenuSfx.play_hover()
|
MenuSfx.play_hover()
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://b7fopt7sx74g8
|
uid://b7fopt7sx74g8
|
||||||
|
|||||||
+112
-112
@@ -1,112 +1,112 @@
|
|||||||
extends Node2D
|
extends Node2D
|
||||||
|
|
||||||
@onready var _tab_bar: TabBar = $MarginContainer/VBoxContainer/TabBar
|
@onready var _tab_bar: TabBar = $MarginContainer/VBoxContainer/TabBar
|
||||||
@onready var _tab_container: TabContainer = $MarginContainer/VBoxContainer/TabContainer
|
@onready var _tab_container: TabContainer = $MarginContainer/VBoxContainer/TabContainer
|
||||||
@onready var _music_volume_slider: HSlider = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicVolumeSlider
|
@onready var _music_volume_slider: HSlider = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicVolumeSlider
|
||||||
@onready var _music_volume_value: Label = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicVolumeValue
|
@onready var _music_volume_value: Label = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicVolumeValue
|
||||||
@onready var _music_mute_checkbox: CheckBox = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicMuteCheckBox
|
@onready var _music_mute_checkbox: CheckBox = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup/MusicMuteCheckBox
|
||||||
@onready var _sfx_volume_slider: HSlider = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxVolumeSlider
|
@onready var _sfx_volume_slider: HSlider = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxVolumeSlider
|
||||||
@onready var _sfx_volume_value: Label = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxVolumeValue
|
@onready var _sfx_volume_value: Label = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxVolumeValue
|
||||||
@onready var _sfx_mute_checkbox: CheckBox = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxMuteCheckBox
|
@onready var _sfx_mute_checkbox: CheckBox = $MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup/SfxMuteCheckBox
|
||||||
@onready var _menu_music: AudioStreamPlayer = get_tree().get_root().get_node_or_null("MenuMusic")
|
@onready var _menu_music: AudioStreamPlayer = get_tree().get_root().get_node_or_null("MenuMusic")
|
||||||
@onready var _menu_sfx: AudioStreamPlayer = get_tree().get_root().get_node_or_null("MenuSfx")
|
@onready var _menu_sfx: AudioStreamPlayer = get_tree().get_root().get_node_or_null("MenuSfx")
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_tab_bar.tab_changed.connect(_on_tab_bar_tab_changed)
|
_tab_bar.tab_changed.connect(_on_tab_bar_tab_changed)
|
||||||
_tab_container.tab_changed.connect(_on_tab_container_tab_changed)
|
_tab_container.tab_changed.connect(_on_tab_container_tab_changed)
|
||||||
_tab_container.current_tab = _tab_bar.current_tab
|
_tab_container.current_tab = _tab_bar.current_tab
|
||||||
_music_volume_slider.value_changed.connect(_on_music_volume_changed)
|
_music_volume_slider.value_changed.connect(_on_music_volume_changed)
|
||||||
_music_mute_checkbox.toggled.connect(_on_music_mute_toggled)
|
_music_mute_checkbox.toggled.connect(_on_music_mute_toggled)
|
||||||
_sfx_volume_slider.value_changed.connect(_on_sfx_volume_changed)
|
_sfx_volume_slider.value_changed.connect(_on_sfx_volume_changed)
|
||||||
_sfx_mute_checkbox.toggled.connect(_on_sfx_mute_toggled)
|
_sfx_mute_checkbox.toggled.connect(_on_sfx_mute_toggled)
|
||||||
_sync_audio_controls()
|
_sync_audio_controls()
|
||||||
_register_focus_sounds()
|
_register_focus_sounds()
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
if event.is_action_pressed("ui_cancel"):
|
if event.is_action_pressed("ui_cancel"):
|
||||||
get_tree().change_scene_to_file("uid://b4k81taauef4q")
|
get_tree().change_scene_to_file("uid://b4k81taauef4q")
|
||||||
|
|
||||||
func _on_tab_bar_tab_changed(tab_index: int) -> void:
|
func _on_tab_bar_tab_changed(tab_index: int) -> void:
|
||||||
if _tab_container.current_tab != tab_index:
|
if _tab_container.current_tab != tab_index:
|
||||||
_tab_container.current_tab = tab_index
|
_tab_container.current_tab = tab_index
|
||||||
|
|
||||||
func _on_tab_container_tab_changed(tab_index: int) -> void:
|
func _on_tab_container_tab_changed(tab_index: int) -> void:
|
||||||
if _tab_bar.current_tab != tab_index:
|
if _tab_bar.current_tab != tab_index:
|
||||||
_tab_bar.current_tab = tab_index
|
_tab_bar.current_tab = tab_index
|
||||||
|
|
||||||
func _sync_audio_controls() -> void:
|
func _sync_audio_controls() -> void:
|
||||||
var value: float = 0.7
|
var value: float = 0.7
|
||||||
var muted: bool = false
|
var muted: bool = false
|
||||||
if _menu_music:
|
if _menu_music:
|
||||||
if _menu_music.has_method("get_user_volume"):
|
if _menu_music.has_method("get_user_volume"):
|
||||||
value = _menu_music.get_user_volume()
|
value = _menu_music.get_user_volume()
|
||||||
if _menu_music.has_method("is_user_muted"):
|
if _menu_music.has_method("is_user_muted"):
|
||||||
muted = _menu_music.is_user_muted()
|
muted = _menu_music.is_user_muted()
|
||||||
_apply_audio_control_state(_music_volume_slider, _music_mute_checkbox, _music_volume_value, value, muted)
|
_apply_audio_control_state(_music_volume_slider, _music_mute_checkbox, _music_volume_value, value, muted)
|
||||||
|
|
||||||
var sfx_value: float = 0.7
|
var sfx_value: float = 0.7
|
||||||
var sfx_muted: bool = false
|
var sfx_muted: bool = false
|
||||||
if _menu_sfx:
|
if _menu_sfx:
|
||||||
if _menu_sfx.has_method("get_user_volume"):
|
if _menu_sfx.has_method("get_user_volume"):
|
||||||
sfx_value = _menu_sfx.get_user_volume()
|
sfx_value = _menu_sfx.get_user_volume()
|
||||||
if _menu_sfx.has_method("is_user_muted"):
|
if _menu_sfx.has_method("is_user_muted"):
|
||||||
sfx_muted = _menu_sfx.is_user_muted()
|
sfx_muted = _menu_sfx.is_user_muted()
|
||||||
_apply_audio_control_state(_sfx_volume_slider, _sfx_mute_checkbox, _sfx_volume_value, sfx_value, sfx_muted)
|
_apply_audio_control_state(_sfx_volume_slider, _sfx_mute_checkbox, _sfx_volume_value, sfx_value, sfx_muted)
|
||||||
|
|
||||||
func _on_music_volume_changed(value: float) -> void:
|
func _on_music_volume_changed(value: float) -> void:
|
||||||
if _menu_music and _menu_music.has_method("set_user_volume"):
|
if _menu_music and _menu_music.has_method("set_user_volume"):
|
||||||
_menu_music.set_user_volume(value)
|
_menu_music.set_user_volume(value)
|
||||||
_update_volume_label(_music_volume_value, value, _music_mute_checkbox.button_pressed)
|
_update_volume_label(_music_volume_value, value, _music_mute_checkbox.button_pressed)
|
||||||
|
|
||||||
func _on_music_mute_toggled(pressed: bool) -> void:
|
func _on_music_mute_toggled(pressed: bool) -> void:
|
||||||
if _menu_music and _menu_music.has_method("set_user_muted"):
|
if _menu_music and _menu_music.has_method("set_user_muted"):
|
||||||
_menu_music.set_user_muted(pressed)
|
_menu_music.set_user_muted(pressed)
|
||||||
_music_volume_slider.editable = not pressed
|
_music_volume_slider.editable = not pressed
|
||||||
_update_volume_label(_music_volume_value, _music_volume_slider.value, pressed)
|
_update_volume_label(_music_volume_value, _music_volume_slider.value, pressed)
|
||||||
|
|
||||||
func _on_sfx_volume_changed(value: float) -> void:
|
func _on_sfx_volume_changed(value: float) -> void:
|
||||||
if _menu_sfx and _menu_sfx.has_method("set_user_volume"):
|
if _menu_sfx and _menu_sfx.has_method("set_user_volume"):
|
||||||
_menu_sfx.set_user_volume(value)
|
_menu_sfx.set_user_volume(value)
|
||||||
_update_volume_label(_sfx_volume_value, value, _sfx_mute_checkbox.button_pressed)
|
_update_volume_label(_sfx_volume_value, value, _sfx_mute_checkbox.button_pressed)
|
||||||
|
|
||||||
func _on_sfx_mute_toggled(pressed: bool) -> void:
|
func _on_sfx_mute_toggled(pressed: bool) -> void:
|
||||||
if _menu_sfx and _menu_sfx.has_method("set_user_muted"):
|
if _menu_sfx and _menu_sfx.has_method("set_user_muted"):
|
||||||
_menu_sfx.set_user_muted(pressed)
|
_menu_sfx.set_user_muted(pressed)
|
||||||
_sfx_volume_slider.editable = not pressed
|
_sfx_volume_slider.editable = not pressed
|
||||||
_update_volume_label(_sfx_volume_value, _sfx_volume_slider.value, pressed)
|
_update_volume_label(_sfx_volume_value, _sfx_volume_slider.value, pressed)
|
||||||
|
|
||||||
func _apply_audio_control_state(slider: HSlider, checkbox: CheckBox, value_label: Label, value: float, muted: bool) -> void:
|
func _apply_audio_control_state(slider: HSlider, checkbox: CheckBox, value_label: Label, value: float, muted: bool) -> void:
|
||||||
slider.set_block_signals(true)
|
slider.set_block_signals(true)
|
||||||
slider.value = value
|
slider.value = value
|
||||||
slider.set_block_signals(false)
|
slider.set_block_signals(false)
|
||||||
slider.editable = not muted
|
slider.editable = not muted
|
||||||
checkbox.set_block_signals(true)
|
checkbox.set_block_signals(true)
|
||||||
checkbox.button_pressed = muted
|
checkbox.button_pressed = muted
|
||||||
checkbox.set_block_signals(false)
|
checkbox.set_block_signals(false)
|
||||||
_update_volume_label(value_label, value, muted)
|
_update_volume_label(value_label, value, muted)
|
||||||
|
|
||||||
func _update_volume_label(value_label: Label, value: float, muted: bool) -> void:
|
func _update_volume_label(value_label: Label, value: float, muted: bool) -> void:
|
||||||
if muted:
|
if muted:
|
||||||
value_label.text = "Muted"
|
value_label.text = "Muted"
|
||||||
else:
|
else:
|
||||||
var percent: int = int(round(value * 100.0))
|
var percent: int = int(round(value * 100.0))
|
||||||
value_label.text = str(percent) + "%"
|
value_label.text = str(percent) + "%"
|
||||||
|
|
||||||
func _register_focus_sounds() -> void:
|
func _register_focus_sounds() -> void:
|
||||||
_connect_focus_recursive(self)
|
_connect_focus_recursive(self)
|
||||||
|
|
||||||
func _connect_focus_recursive(node: Node) -> void:
|
func _connect_focus_recursive(node: Node) -> void:
|
||||||
if node is Control:
|
if node is Control:
|
||||||
var control: Control = node
|
var control: Control = node
|
||||||
if control.focus_mode != Control.FOCUS_NONE:
|
if control.focus_mode != Control.FOCUS_NONE:
|
||||||
if not control.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")):
|
if not control.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")):
|
||||||
control.focus_entered.connect(_on_menu_item_focus)
|
control.focus_entered.connect(_on_menu_item_focus)
|
||||||
if control.has_signal("mouse_entered") and (control is BaseButton or control.focus_mode != Control.FOCUS_NONE):
|
if control.has_signal("mouse_entered") and (control is BaseButton or control.focus_mode != Control.FOCUS_NONE):
|
||||||
if not control.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")):
|
if not control.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")):
|
||||||
control.mouse_entered.connect(_on_menu_item_focus)
|
control.mouse_entered.connect(_on_menu_item_focus)
|
||||||
for child in node.get_children():
|
for child in node.get_children():
|
||||||
_connect_focus_recursive(child)
|
_connect_focus_recursive(child)
|
||||||
|
|
||||||
func _on_menu_item_focus() -> void:
|
func _on_menu_item_focus() -> void:
|
||||||
if MenuSfx:
|
if MenuSfx:
|
||||||
MenuSfx.play_hover()
|
MenuSfx.play_hover()
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://h1slqbemgwvr
|
uid://h1slqbemgwvr
|
||||||
|
|||||||
+157
-157
@@ -1,157 +1,157 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://d3tqrm4ry4l88"]
|
[gd_scene load_steps=5 format=3 uid="uid://d3tqrm4ry4l88"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://h1slqbemgwvr" path="res://scenes/UI/Settings.gd" id="1_1dggd"]
|
[ext_resource type="Script" uid="uid://h1slqbemgwvr" path="res://scenes/UI/Settings.gd" id="1_1dggd"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="1_i47rn"]
|
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="1_i47rn"]
|
||||||
[ext_resource type="FontFile" uid="uid://m5ceou0rk6j6" path="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" id="2_46duy"]
|
[ext_resource type="FontFile" uid="uid://m5ceou0rk6j6" path="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" id="2_46duy"]
|
||||||
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="3_46duy"]
|
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="3_46duy"]
|
||||||
|
|
||||||
|
|
||||||
[node name="settings_screen" type="Node2D"]
|
[node name="settings_screen" type="Node2D"]
|
||||||
script = ExtResource("1_1dggd")
|
script = ExtResource("1_1dggd")
|
||||||
metadata/_edit_vertical_guides_ = [1152.0]
|
metadata/_edit_vertical_guides_ = [1152.0]
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
[node name="TextureRect" type="TextureRect" parent="."]
|
||||||
offset_left = -192.0
|
offset_left = -192.0
|
||||||
offset_top = -188.0
|
offset_top = -188.0
|
||||||
offset_right = 1344.0
|
offset_right = 1344.0
|
||||||
offset_bottom = 836.0
|
offset_bottom = 836.0
|
||||||
texture = ExtResource("1_i47rn")
|
texture = ExtResource("1_i47rn")
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||||
offset_right = 1152.0
|
offset_right = 1152.0
|
||||||
offset_bottom = 648.0
|
offset_bottom = 648.0
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
|
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme_override_fonts/font = ExtResource("2_46duy")
|
theme_override_fonts/font = ExtResource("2_46duy")
|
||||||
theme_override_font_sizes/font_size = 42
|
theme_override_font_sizes/font_size = 42
|
||||||
text = "Settings"
|
text = "Settings"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="TabBar" type="TabBar" parent="MarginContainer/VBoxContainer"]
|
[node name="TabBar" type="TabBar" parent="MarginContainer/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme = ExtResource("3_46duy")
|
theme = ExtResource("3_46duy")
|
||||||
current_tab = 0
|
current_tab = 0
|
||||||
tab_count = 4
|
tab_count = 4
|
||||||
tab_0/title = "Gameplay"
|
tab_0/title = "Gameplay"
|
||||||
tab_1/title = "Video"
|
tab_1/title = "Video"
|
||||||
tab_2/title = "Audio"
|
tab_2/title = "Audio"
|
||||||
tab_3/title = "Dev"
|
tab_3/title = "Dev"
|
||||||
|
|
||||||
[node name="TabContainer" type="TabContainer" parent="MarginContainer/VBoxContainer"]
|
[node name="TabContainer" type="TabContainer" parent="MarginContainer/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
tabs_visible = false
|
tabs_visible = false
|
||||||
|
|
||||||
[node name="Gameplay" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
[node name="Gameplay" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="Video" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
[node name="Video" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="Audio" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
[node name="Audio" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="AudioVBox" type="VBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio"]
|
[node name="AudioVBox" type="VBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 10
|
||||||
offset_left = 120.0
|
offset_left = 120.0
|
||||||
offset_top = 240.0
|
offset_top = 240.0
|
||||||
offset_right = -120.0
|
offset_right = -120.0
|
||||||
offset_bottom = -40.0
|
offset_bottom = -40.0
|
||||||
|
|
||||||
[node name="MusicVolumeLabel" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
[node name="MusicVolumeLabel" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Music Volume"
|
text = "Music Volume"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
[node name="MusicVolumeGroup" type="HBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
[node name="MusicVolumeGroup" type="HBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 12
|
||||||
alignment = 1
|
alignment = 1
|
||||||
custom_minimum_size = Vector2(0, 40)
|
custom_minimum_size = Vector2(0, 40)
|
||||||
|
|
||||||
[node name="MusicVolumeSlider" type="HSlider" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup"]
|
[node name="MusicVolumeSlider" type="HSlider" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
min_value = 0.0
|
min_value = 0.0
|
||||||
max_value = 1.0
|
max_value = 1.0
|
||||||
step = 0.01
|
step = 0.01
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
custom_minimum_size = Vector2(320, 0)
|
custom_minimum_size = Vector2(320, 0)
|
||||||
|
|
||||||
[node name="MusicVolumeValue" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup"]
|
[node name="MusicVolumeValue" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "70%"
|
text = "70%"
|
||||||
size_flags_horizontal = 1
|
size_flags_horizontal = 1
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
custom_minimum_size = Vector2(60, 0)
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
|
||||||
[node name="MusicMuteCheckBox" type="CheckBox" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup"]
|
[node name="MusicMuteCheckBox" type="CheckBox" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/MusicVolumeGroup"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Mute"
|
text = "Mute"
|
||||||
size_flags_horizontal = 1
|
size_flags_horizontal = 1
|
||||||
|
|
||||||
[node name="SfxVolumeLabel" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
[node name="SfxVolumeLabel" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Menu SFX Volume"
|
text = "Menu SFX Volume"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
|
|
||||||
[node name="SfxVolumeGroup" type="HBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
[node name="SfxVolumeGroup" type="HBoxContainer" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 12
|
||||||
alignment = 1
|
alignment = 1
|
||||||
custom_minimum_size = Vector2(0, 40)
|
custom_minimum_size = Vector2(0, 40)
|
||||||
|
|
||||||
[node name="SfxVolumeSlider" type="HSlider" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"]
|
[node name="SfxVolumeSlider" type="HSlider" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
min_value = 0.0
|
min_value = 0.0
|
||||||
max_value = 1.0
|
max_value = 1.0
|
||||||
step = 0.01
|
step = 0.01
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
custom_minimum_size = Vector2(320, 0)
|
custom_minimum_size = Vector2(320, 0)
|
||||||
|
|
||||||
[node name="SfxVolumeValue" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"]
|
[node name="SfxVolumeValue" type="Label" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "70%"
|
text = "70%"
|
||||||
size_flags_horizontal = 1
|
size_flags_horizontal = 1
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
custom_minimum_size = Vector2(60, 0)
|
custom_minimum_size = Vector2(60, 0)
|
||||||
|
|
||||||
[node name="SfxMuteCheckBox" type="CheckBox" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"]
|
[node name="SfxMuteCheckBox" type="CheckBox" parent="MarginContainer/VBoxContainer/TabContainer/Audio/AudioVBox/SfxVolumeGroup"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Mute"
|
text = "Mute"
|
||||||
size_flags_horizontal = 1
|
size_flags_horizontal = 1
|
||||||
|
|
||||||
[node name="Dev" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
[node name="Dev" type="Control" parent="MarginContainer/VBoxContainer/TabContainer"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
var is_logged_in: bool = false
|
var is_logged_in: bool = false
|
||||||
var access_token: String = ""
|
var access_token: String = ""
|
||||||
var username: String = ""
|
var username: String = ""
|
||||||
|
|
||||||
func set_session(new_username: String, token: String) -> void:
|
func set_session(new_username: String, token: String) -> void:
|
||||||
is_logged_in = true
|
is_logged_in = true
|
||||||
username = new_username
|
username = new_username
|
||||||
access_token = token
|
access_token = token
|
||||||
|
|
||||||
func clear_session() -> void:
|
func clear_session() -> void:
|
||||||
is_logged_in = false
|
is_logged_in = false
|
||||||
username = ""
|
username = ""
|
||||||
access_token = ""
|
access_token = ""
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://ccloj2rh4dche
|
uid://ccloj2rh4dche
|
||||||
|
|||||||
+132
-117
@@ -1,79 +1,107 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
const AUTH_LOGOUT_URL := "https://pauth.ranaze.com/api/Auth/logout"
|
const AUTH_LOGOUT_URL := "https://pauth.ranaze.com/api/Auth/logout"
|
||||||
|
|
||||||
@onready var _status_label: Label = %StatusLabel
|
@onready var _status_label: Label = %StatusLabel
|
||||||
@onready var _character_list: ItemList = %CharacterList
|
@onready var _character_list: ItemList = %CharacterList
|
||||||
@onready var _name_input: LineEdit = %NameInput
|
@onready var _name_input: LineEdit = %NameInput
|
||||||
@onready var _logout_request: HTTPRequest = %LogoutRequest
|
@onready var _logout_request: HTTPRequest = %LogoutRequest
|
||||||
|
|
||||||
var _characters: Array = []
|
var _characters: Array = []
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
if not AuthState.is_logged_in:
|
if not AuthState.is_logged_in:
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
||||||
return
|
return
|
||||||
_load_characters()
|
_load_characters()
|
||||||
|
|
||||||
func _log_failure(action: String, response: Dictionary) -> void:
|
func _log_failure(action: String, response: Dictionary) -> void:
|
||||||
push_warning("%s failed: status=%s error=%s body=%s" % [
|
push_warning("%s failed: status=%s error=%s body=%s" % [
|
||||||
action,
|
action,
|
||||||
response.get("status", "n/a"),
|
response.get("status", "n/a"),
|
||||||
response.get("error", ""),
|
response.get("error", ""),
|
||||||
response.get("body", "")
|
response.get("body", "")
|
||||||
])
|
])
|
||||||
|
|
||||||
func _load_characters() -> void:
|
func _load_characters() -> void:
|
||||||
_status_label.text = "Loading characters..."
|
_status_label.text = "Loading characters..."
|
||||||
var response := await CharacterService.list_characters()
|
var response := await CharacterService.list_characters()
|
||||||
if not response.get("ok", false):
|
if not response.get("ok", false):
|
||||||
_log_failure("List characters", response)
|
_log_failure("List characters", response)
|
||||||
_status_label.text = "Failed to load characters."
|
_status_label.text = "Failed to load characters."
|
||||||
return
|
return
|
||||||
|
|
||||||
var parsed: Variant = JSON.parse_string(String(response.get("body", "")))
|
var parsed: Variant = JSON.parse_string(String(response.get("body", "")))
|
||||||
if typeof(parsed) != TYPE_ARRAY:
|
if typeof(parsed) != TYPE_ARRAY:
|
||||||
_status_label.text = "Unexpected character response."
|
_status_label.text = "Unexpected character response."
|
||||||
return
|
return
|
||||||
|
|
||||||
_characters = parsed
|
_characters = parsed
|
||||||
_character_list.clear()
|
_character_list.clear()
|
||||||
for character in _characters:
|
for character in _characters:
|
||||||
var character_name := String(character.get("name", "Unnamed"))
|
var character_name := String(character.get("name", "Unnamed"))
|
||||||
_character_list.add_item(character_name)
|
_character_list.add_item(character_name)
|
||||||
|
|
||||||
if _characters.is_empty():
|
if _characters.is_empty():
|
||||||
_status_label.text = "No characters yet. Add one below."
|
_status_label.text = "No characters yet. Add one below."
|
||||||
else:
|
else:
|
||||||
_status_label.text = ""
|
_status_label.text = ""
|
||||||
|
|
||||||
func _on_add_button_pressed() -> void:
|
func _on_add_button_pressed() -> void:
|
||||||
var character_name := _name_input.text.strip_edges()
|
var character_name := _name_input.text.strip_edges()
|
||||||
if character_name.is_empty():
|
if character_name.is_empty():
|
||||||
_status_label.text = "Enter a character name first."
|
_status_label.text = "Enter a character name first."
|
||||||
return
|
return
|
||||||
|
|
||||||
_status_label.text = "Creating character..."
|
_status_label.text = "Creating character..."
|
||||||
var response := await CharacterService.create_character(character_name)
|
var response := await CharacterService.create_character(character_name)
|
||||||
if not response.get("ok", false):
|
if not response.get("ok", false):
|
||||||
_log_failure("Create character", response)
|
_log_failure("Create character", response)
|
||||||
_status_label.text = "Failed to create character."
|
_status_label.text = "Failed to create character."
|
||||||
return
|
return
|
||||||
|
|
||||||
var parsed: Variant = JSON.parse_string(String(response.get("body", "")))
|
var parsed: Variant = JSON.parse_string(String(response.get("body", "")))
|
||||||
if typeof(parsed) == TYPE_DICTIONARY:
|
if typeof(parsed) == TYPE_DICTIONARY:
|
||||||
_characters.append(parsed)
|
_characters.append(parsed)
|
||||||
_character_list.add_item(String(parsed.get("name", character_name)))
|
_character_list.add_item(String(parsed.get("name", character_name)))
|
||||||
_name_input.text = ""
|
_name_input.text = ""
|
||||||
_status_label.text = "Character added."
|
_status_label.text = "Character added."
|
||||||
else:
|
else:
|
||||||
_status_label.text = "Character created, but response was unexpected."
|
_status_label.text = "Character created, but response was unexpected."
|
||||||
|
|
||||||
func _on_delete_button_pressed() -> void:
|
func _on_delete_button_pressed() -> void:
|
||||||
var selected := _character_list.get_selected_items()
|
var selected := _character_list.get_selected_items()
|
||||||
if selected.is_empty():
|
if selected.is_empty():
|
||||||
_status_label.text = "Select a character to delete."
|
_status_label.text = "Select a character to delete."
|
||||||
return
|
return
|
||||||
|
|
||||||
|
var index := selected[0]
|
||||||
|
if index < 0 or index >= _characters.size():
|
||||||
|
_status_label.text = "Invalid selection."
|
||||||
|
return
|
||||||
|
|
||||||
|
var character: Dictionary = _characters[index]
|
||||||
|
var character_id := String(character.get("id", character.get("Id", "")))
|
||||||
|
if character_id.is_empty():
|
||||||
|
_status_label.text = "Missing character id."
|
||||||
|
return
|
||||||
|
|
||||||
|
_status_label.text = "Deleting character..."
|
||||||
|
var response := await CharacterService.delete_character(character_id)
|
||||||
|
if not response.get("ok", false):
|
||||||
|
_log_failure("Delete character", response)
|
||||||
|
_status_label.text = "Failed to delete character."
|
||||||
|
return
|
||||||
|
|
||||||
|
_characters.remove_at(index)
|
||||||
|
_character_list.remove_item(index)
|
||||||
|
_status_label.text = "Character deleted."
|
||||||
|
|
||||||
|
func _on_select_button_pressed() -> void:
|
||||||
|
var selected := _character_list.get_selected_items()
|
||||||
|
if selected.is_empty():
|
||||||
|
_status_label.text = "Select a character first."
|
||||||
|
return
|
||||||
|
|
||||||
var index := selected[0]
|
var index := selected[0]
|
||||||
if index < 0 or index >= _characters.size():
|
if index < 0 or index >= _characters.size():
|
||||||
@@ -81,49 +109,36 @@ func _on_delete_button_pressed() -> void:
|
|||||||
return
|
return
|
||||||
|
|
||||||
var character: Dictionary = _characters[index]
|
var character: Dictionary = _characters[index]
|
||||||
var character_id := String(character.get("id", character.get("Id", "")))
|
SelectedCharacter.set_character(character)
|
||||||
if character_id.is_empty():
|
get_tree().change_scene_to_file("res://scenes/Levels/location_level.tscn")
|
||||||
_status_label.text = "Missing character id."
|
|
||||||
return
|
func _on_refresh_button_pressed() -> void:
|
||||||
|
_load_characters()
|
||||||
_status_label.text = "Deleting character..."
|
|
||||||
var response := await CharacterService.delete_character(character_id)
|
func _on_back_button_pressed() -> void:
|
||||||
if not response.get("ok", false):
|
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
||||||
_log_failure("Delete character", response)
|
|
||||||
_status_label.text = "Failed to delete character."
|
func _on_logout_button_pressed() -> void:
|
||||||
return
|
_request_logout()
|
||||||
|
|
||||||
_characters.remove_at(index)
|
func _request_logout() -> void:
|
||||||
_character_list.remove_item(index)
|
if AuthState.access_token.is_empty():
|
||||||
_status_label.text = "Character deleted."
|
AuthState.clear_session()
|
||||||
|
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
||||||
func _on_refresh_button_pressed() -> void:
|
return
|
||||||
_load_characters()
|
|
||||||
|
var headers := PackedStringArray([
|
||||||
func _on_back_button_pressed() -> void:
|
"Authorization: Bearer %s" % AuthState.access_token,
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
])
|
||||||
|
var err := _logout_request.request(AUTH_LOGOUT_URL, headers, HTTPClient.METHOD_POST)
|
||||||
func _on_logout_button_pressed() -> void:
|
if err != OK:
|
||||||
_request_logout()
|
push_warning("Failed to send logout request: %s" % err)
|
||||||
|
AuthState.clear_session()
|
||||||
func _request_logout() -> void:
|
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
||||||
if AuthState.access_token.is_empty():
|
|
||||||
AuthState.clear_session()
|
func _on_logout_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
var body_text := body.get_string_from_utf8()
|
||||||
return
|
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
||||||
|
push_warning("Logout failed (%s/%s): %s" % [result, response_code, body_text])
|
||||||
var headers := PackedStringArray([
|
AuthState.clear_session()
|
||||||
"Authorization: Bearer %s" % AuthState.access_token,
|
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
||||||
])
|
|
||||||
var err := _logout_request.request(AUTH_LOGOUT_URL, headers, HTTPClient.METHOD_POST)
|
|
||||||
if err != OK:
|
|
||||||
push_warning("Failed to send logout request: %s" % err)
|
|
||||||
AuthState.clear_session()
|
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
|
||||||
|
|
||||||
func _on_logout_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
|
||||||
var body_text := body.get_string_from_utf8()
|
|
||||||
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
|
||||||
push_warning("Logout failed (%s/%s): %s" % [result, response_code, body_text])
|
|
||||||
AuthState.clear_session()
|
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://c2y7ftq2k3v4x
|
uid://c2y7ftq2k3v4x
|
||||||
|
|||||||
@@ -1,131 +1,139 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://cw3b7n7k2c4h6"]
|
[gd_scene load_steps=5 format=3 uid="uid://cw3b7n7k2c4h6"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scenes/UI/character_screen.gd" id="1_0p3qk"]
|
[ext_resource type="Script" path="res://scenes/UI/character_screen.gd" id="1_0p3qk"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="2_5g2t1"]
|
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="2_5g2t1"]
|
||||||
[ext_resource type="Theme" uid="uid://tn8qndst18d6" path="res://themes/title_font_theme.tres" id="3_k2j6k"]
|
[ext_resource type="Theme" uid="uid://tn8qndst18d6" path="res://themes/title_font_theme.tres" id="3_k2j6k"]
|
||||||
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="4_5b3b7"]
|
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="4_5b3b7"]
|
||||||
|
|
||||||
[node name="CharacterScreen" type="Control"]
|
[node name="CharacterScreen" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
script = ExtResource("1_0p3qk")
|
script = ExtResource("1_0p3qk")
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
[node name="TextureRect" type="TextureRect" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
texture = ExtResource("2_5g2t1")
|
texture = ExtResource("2_5g2t1")
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
theme_override_constants/margin_left = 80
|
theme_override_constants/margin_left = 80
|
||||||
theme_override_constants/margin_top = 40
|
theme_override_constants/margin_top = 40
|
||||||
theme_override_constants/margin_right = 80
|
theme_override_constants/margin_right = 80
|
||||||
theme_override_constants/margin_bottom = 40
|
theme_override_constants/margin_bottom = 40
|
||||||
|
|
||||||
[node name="ContentCenter" type="CenterContainer" parent="MarginContainer"]
|
[node name="ContentCenter" type="CenterContainer" parent="MarginContainer"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="ContentVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter"]
|
[node name="ContentVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
theme_override_constants/separation = 18
|
theme_override_constants/separation = 18
|
||||||
|
|
||||||
[node name="TitleLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="TitleLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("3_k2j6k")
|
theme = ExtResource("3_k2j6k")
|
||||||
text = "YOUR CHARACTERS"
|
text = "YOUR CHARACTERS"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="StatusLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="StatusLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="CharacterList" type="ItemList" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="CharacterList" type="ItemList" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
custom_minimum_size = Vector2(520, 240)
|
custom_minimum_size = Vector2(520, 240)
|
||||||
|
|
||||||
[node name="AddHBox" type="HBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="AddHBox" type="HBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
[node name="NameInput" type="LineEdit" parent="MarginContainer/ContentCenter/ContentVBox/AddHBox"]
|
[node name="NameInput" type="LineEdit" parent="MarginContainer/ContentCenter/ContentVBox/AddHBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 3
|
size_flags_horizontal = 3
|
||||||
placeholder_text = "character name"
|
placeholder_text = "character name"
|
||||||
|
|
||||||
[node name="AddButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/AddHBox"]
|
[node name="AddButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/AddHBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 0
|
size_flags_horizontal = 0
|
||||||
theme = ExtResource("4_5b3b7")
|
theme = ExtResource("4_5b3b7")
|
||||||
text = "ADD"
|
text = "ADD"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="ActionHBox" type="HBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="ActionHBox" type="HBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
|
[node name="SelectButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 4
|
||||||
|
theme = ExtResource("4_5b3b7")
|
||||||
|
text = "SELECT"
|
||||||
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="RefreshButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
[node name="RefreshButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_5b3b7")
|
theme = ExtResource("4_5b3b7")
|
||||||
text = "REFRESH"
|
text = "REFRESH"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="DeleteButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
[node name="DeleteButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_5b3b7")
|
theme = ExtResource("4_5b3b7")
|
||||||
text = "DELETE"
|
text = "DELETE"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="BackButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
[node name="BackButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_5b3b7")
|
theme = ExtResource("4_5b3b7")
|
||||||
text = "BACK"
|
text = "BACK"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="LogoutButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
[node name="LogoutButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ActionHBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_5b3b7")
|
theme = ExtResource("4_5b3b7")
|
||||||
text = "LOG OUT"
|
text = "LOG OUT"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="LogoutRequest" type="HTTPRequest" parent="."]
|
[node name="LogoutRequest" type="HTTPRequest" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/AddHBox/AddButton" to="." method="_on_add_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/AddHBox/AddButton" to="." method="_on_add_button_pressed"]
|
||||||
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/SelectButton" to="." method="_on_select_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/RefreshButton" to="." method="_on_refresh_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/RefreshButton" to="." method="_on_refresh_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/DeleteButton" to="." method="_on_delete_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/DeleteButton" to="." method="_on_delete_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/BackButton" to="." method="_on_back_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/BackButton" to="." method="_on_back_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/LogoutButton" to="." method="_on_logout_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ActionHBox/LogoutButton" to="." method="_on_logout_button_pressed"]
|
||||||
[connection signal="request_completed" from="LogoutRequest" to="." method="_on_logout_request_completed"]
|
[connection signal="request_completed" from="LogoutRequest" to="." method="_on_logout_request_completed"]
|
||||||
|
|||||||
@@ -1,58 +1,58 @@
|
|||||||
extends Node
|
extends Node
|
||||||
|
|
||||||
const CHARACTER_API_URL := "https://pcharacter.ranaze.com/api/Characters"
|
const CHARACTER_API_URL := "https://pchar.ranaze.com/api/Characters"
|
||||||
|
|
||||||
func list_characters() -> Dictionary:
|
func list_characters() -> Dictionary:
|
||||||
return await _request(HTTPClient.METHOD_GET, CHARACTER_API_URL)
|
return await _request(HTTPClient.METHOD_GET, CHARACTER_API_URL)
|
||||||
|
|
||||||
func create_character(character_name: String) -> Dictionary:
|
func create_character(character_name: String) -> Dictionary:
|
||||||
var payload := JSON.stringify({
|
var payload := JSON.stringify({
|
||||||
"name": character_name
|
"name": character_name
|
||||||
})
|
})
|
||||||
return await _request(HTTPClient.METHOD_POST, CHARACTER_API_URL, payload)
|
return await _request(HTTPClient.METHOD_POST, CHARACTER_API_URL, payload)
|
||||||
|
|
||||||
func delete_character(character_id: String) -> Dictionary:
|
func delete_character(character_id: String) -> Dictionary:
|
||||||
var url := "%s/%s" % [CHARACTER_API_URL, character_id]
|
var url := "%s/%s" % [CHARACTER_API_URL, character_id]
|
||||||
return await _request(HTTPClient.METHOD_DELETE, url)
|
return await _request(HTTPClient.METHOD_DELETE, url)
|
||||||
|
|
||||||
func _request(method: int, url: String, body: String = "") -> Dictionary:
|
func _request(method: int, url: String, body: String = "") -> Dictionary:
|
||||||
var request := HTTPRequest.new()
|
var request := HTTPRequest.new()
|
||||||
add_child(request)
|
add_child(request)
|
||||||
|
|
||||||
var headers := PackedStringArray()
|
var headers := PackedStringArray()
|
||||||
if not AuthState.access_token.is_empty():
|
if not AuthState.access_token.is_empty():
|
||||||
headers.append("Authorization: Bearer %s" % AuthState.access_token)
|
headers.append("Authorization: Bearer %s" % AuthState.access_token)
|
||||||
if method == HTTPClient.METHOD_POST or method == HTTPClient.METHOD_PUT:
|
if method == HTTPClient.METHOD_POST or method == HTTPClient.METHOD_PUT:
|
||||||
headers.append("Content-Type: application/json")
|
headers.append("Content-Type: application/json")
|
||||||
|
|
||||||
var err := request.request(url, headers, method, body)
|
var err := request.request(url, headers, method, body)
|
||||||
if err != OK:
|
if err != OK:
|
||||||
request.queue_free()
|
request.queue_free()
|
||||||
return {
|
return {
|
||||||
"ok": false,
|
"ok": false,
|
||||||
"status": 0,
|
"status": 0,
|
||||||
"error": "Failed to send request (%s)." % err,
|
"error": "Failed to send request (%s)." % err,
|
||||||
"body": ""
|
"body": ""
|
||||||
}
|
}
|
||||||
|
|
||||||
var result: Array = await request.request_completed
|
var result: Array = await request.request_completed
|
||||||
request.queue_free()
|
request.queue_free()
|
||||||
|
|
||||||
var result_code: int = result[0]
|
var result_code: int = result[0]
|
||||||
var response_code: int = result[1]
|
var response_code: int = result[1]
|
||||||
var response_body: String = result[3].get_string_from_utf8()
|
var response_body: String = result[3].get_string_from_utf8()
|
||||||
|
|
||||||
if result_code != HTTPRequest.RESULT_SUCCESS:
|
if result_code != HTTPRequest.RESULT_SUCCESS:
|
||||||
return {
|
return {
|
||||||
"ok": false,
|
"ok": false,
|
||||||
"status": response_code,
|
"status": response_code,
|
||||||
"error": "Network error (%s)." % result_code,
|
"error": "Network error (%s)." % result_code,
|
||||||
"body": response_body
|
"body": response_body
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"ok": response_code >= 200 and response_code < 300,
|
"ok": response_code >= 200 and response_code < 300,
|
||||||
"status": response_code,
|
"status": response_code,
|
||||||
"error": "",
|
"error": "",
|
||||||
"body": response_body
|
"body": response_body
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://c8kchv0e77yw4
|
uid://c8kchv0e77yw4
|
||||||
|
|||||||
@@ -1,48 +1,48 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
const AUTH_LOGIN_URL := "https://pauth.ranaze.com/api/Auth/login"
|
const AUTH_LOGIN_URL := "https://pauth.ranaze.com/api/Auth/login"
|
||||||
|
|
||||||
@onready var _username_input: LineEdit = %UsernameInput
|
@onready var _username_input: LineEdit = %UsernameInput
|
||||||
@onready var _password_input: LineEdit = %PasswordInput
|
@onready var _password_input: LineEdit = %PasswordInput
|
||||||
@onready var _login_request: HTTPRequest = %LoginRequest
|
@onready var _login_request: HTTPRequest = %LoginRequest
|
||||||
@onready var _error_label: Label = %ErrorLabel
|
@onready var _error_label: Label = %ErrorLabel
|
||||||
|
|
||||||
func _on_log_in_button_pressed() -> void:
|
func _on_log_in_button_pressed() -> void:
|
||||||
var username := _username_input.text.strip_edges()
|
var username := _username_input.text.strip_edges()
|
||||||
var password := _password_input.text
|
var password := _password_input.text
|
||||||
if username.is_empty() or password.is_empty():
|
if username.is_empty() or password.is_empty():
|
||||||
_show_error("Username and password required.")
|
_show_error("Username and password required.")
|
||||||
return
|
return
|
||||||
|
|
||||||
var payload := {
|
var payload := {
|
||||||
"username": username,
|
"username": username,
|
||||||
"password": password,
|
"password": password,
|
||||||
}
|
}
|
||||||
var headers := PackedStringArray(["Content-Type: application/json"])
|
var headers := PackedStringArray(["Content-Type: application/json"])
|
||||||
var err := _login_request.request(AUTH_LOGIN_URL, headers, HTTPClient.METHOD_POST, JSON.stringify(payload))
|
var err := _login_request.request(AUTH_LOGIN_URL, headers, HTTPClient.METHOD_POST, JSON.stringify(payload))
|
||||||
if err != OK:
|
if err != OK:
|
||||||
_show_error("Failed to send login request.")
|
_show_error("Failed to send login request.")
|
||||||
|
|
||||||
func _on_login_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
func _on_login_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||||
var body_text := body.get_string_from_utf8()
|
var body_text := body.get_string_from_utf8()
|
||||||
if result != HTTPRequest.RESULT_SUCCESS:
|
if result != HTTPRequest.RESULT_SUCCESS:
|
||||||
_show_error("Network error. Please try again.")
|
_show_error("Network error. Please try again.")
|
||||||
return
|
return
|
||||||
|
|
||||||
if response_code >= 200 and response_code < 300:
|
if response_code >= 200 and response_code < 300:
|
||||||
var response: Variant = JSON.parse_string(body_text)
|
var response: Variant = JSON.parse_string(body_text)
|
||||||
if typeof(response) == TYPE_DICTIONARY:
|
if typeof(response) == TYPE_DICTIONARY:
|
||||||
#print("Login success for %s" % response.get("username", "unknown"))
|
#print("Login success for %s" % response.get("username", "unknown"))
|
||||||
#print("Access Token: %s" % response.get("accessToken", ""))
|
#print("Access Token: %s" % response.get("accessToken", ""))
|
||||||
var token := String(response.get("accessToken", ""))
|
var token := String(response.get("accessToken", ""))
|
||||||
var username := String(response.get("username", ""))
|
var username := String(response.get("username", ""))
|
||||||
AuthState.set_session(username, token)
|
AuthState.set_session(username, token)
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/character_screen.tscn")
|
get_tree().change_scene_to_file("res://scenes/UI/character_screen.tscn")
|
||||||
else:
|
else:
|
||||||
_show_error("Login failed. Check your credentials.")
|
_show_error("Login failed. Check your credentials.")
|
||||||
|
|
||||||
func _on_back_button_pressed() -> void:
|
func _on_back_button_pressed() -> void:
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
get_tree().change_scene_to_file("res://scenes/UI/start_screen.tscn")
|
||||||
|
|
||||||
func _show_error(message: String) -> void:
|
func _show_error(message: String) -> void:
|
||||||
_error_label.text = message
|
_error_label.text = message
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bnrhapdcfvp04
|
uid://bnrhapdcfvp04
|
||||||
|
|||||||
+117
-117
@@ -1,117 +1,117 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://fmp1tah03kew"]
|
[gd_scene load_steps=5 format=3 uid="uid://fmp1tah03kew"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scenes/UI/login_screen.gd" id="1_jqkpi"]
|
[ext_resource type="Script" path="res://scenes/UI/login_screen.gd" id="1_jqkpi"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="2_2n6di"]
|
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="2_2n6di"]
|
||||||
[ext_resource type="Theme" uid="uid://tn8qndst18d6" path="res://themes/title_font_theme.tres" id="3_c4k70"]
|
[ext_resource type="Theme" uid="uid://tn8qndst18d6" path="res://themes/title_font_theme.tres" id="3_c4k70"]
|
||||||
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="4_gx673"]
|
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="4_gx673"]
|
||||||
|
|
||||||
[node name="LoginScreen" type="Control"]
|
[node name="LoginScreen" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
script = ExtResource("1_jqkpi")
|
script = ExtResource("1_jqkpi")
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
[node name="TextureRect" type="TextureRect" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
texture = ExtResource("2_2n6di")
|
texture = ExtResource("2_2n6di")
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
theme_override_constants/margin_left = 80
|
theme_override_constants/margin_left = 80
|
||||||
theme_override_constants/margin_top = 40
|
theme_override_constants/margin_top = 40
|
||||||
theme_override_constants/margin_right = 80
|
theme_override_constants/margin_right = 80
|
||||||
theme_override_constants/margin_bottom = 40
|
theme_override_constants/margin_bottom = 40
|
||||||
|
|
||||||
[node name="ContentCenter" type="CenterContainer" parent="MarginContainer"]
|
[node name="ContentCenter" type="CenterContainer" parent="MarginContainer"]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
|
|
||||||
[node name="ContentVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter"]
|
[node name="ContentVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
theme_override_constants/separation = 24
|
theme_override_constants/separation = 24
|
||||||
|
|
||||||
[node name="TitleLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="TitleLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("3_c4k70")
|
theme = ExtResource("3_c4k70")
|
||||||
text = "ACCOUNT LOGIN"
|
text = "ACCOUNT LOGIN"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="FormVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="FormVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 8
|
theme_override_constants/separation = 8
|
||||||
custom_minimum_size = Vector2(480, 0)
|
custom_minimum_size = Vector2(480, 0)
|
||||||
|
|
||||||
[node name="UsernameLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
[node name="UsernameLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Username"
|
text = "Username"
|
||||||
|
|
||||||
[node name="UsernameInput" type="LineEdit" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
[node name="UsernameInput" type="LineEdit" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
placeholder_text = "enter username"
|
placeholder_text = "enter username"
|
||||||
caret_blink = true
|
caret_blink = true
|
||||||
|
|
||||||
[node name="PasswordLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
[node name="PasswordLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Password"
|
text = "Password"
|
||||||
|
|
||||||
[node name="PasswordInput" type="LineEdit" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
[node name="PasswordInput" type="LineEdit" parent="MarginContainer/ContentCenter/ContentVBox/FormVBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
placeholder_text = "enter password"
|
placeholder_text = "enter password"
|
||||||
secret = true
|
secret = true
|
||||||
|
|
||||||
[node name="ButtonVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
[node name="ButtonVBox" type="VBoxContainer" parent="MarginContainer/ContentCenter/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme_override_constants/separation = 12
|
theme_override_constants/separation = 12
|
||||||
|
|
||||||
[node name="ErrorLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox/ButtonVBox"]
|
[node name="ErrorLabel" type="Label" parent="MarginContainer/ContentCenter/ContentVBox/ButtonVBox"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
theme = ExtResource("3_c4k70")
|
theme = ExtResource("3_c4k70")
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
theme_override_font_sizes/font_size = 26
|
theme_override_font_sizes/font_size = 26
|
||||||
theme_override_colors/font_color = Color(1, 0.2, 0.2, 1)
|
theme_override_colors/font_color = Color(1, 0.2, 0.2, 1)
|
||||||
|
|
||||||
[node name="LogInButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ButtonVBox"]
|
[node name="LogInButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ButtonVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_gx673")
|
theme = ExtResource("4_gx673")
|
||||||
text = "LOG IN"
|
text = "LOG IN"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="BackButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ButtonVBox"]
|
[node name="BackButton" type="Button" parent="MarginContainer/ContentCenter/ContentVBox/ButtonVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_gx673")
|
theme = ExtResource("4_gx673")
|
||||||
text = "BACK"
|
text = "BACK"
|
||||||
text_alignment = 1
|
text_alignment = 1
|
||||||
|
|
||||||
[node name="LoginRequest" type="HTTPRequest" parent="."]
|
[node name="LoginRequest" type="HTTPRequest" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ButtonVBox/LogInButton" to="." method="_on_log_in_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ButtonVBox/LogInButton" to="." method="_on_log_in_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ButtonVBox/BackButton" to="." method="_on_back_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/ContentCenter/ContentVBox/ButtonVBox/BackButton" to="." method="_on_back_button_pressed"]
|
||||||
[connection signal="request_completed" from="LoginRequest" to="." method="_on_login_request_completed"]
|
[connection signal="request_completed" from="LoginRequest" to="." method="_on_login_request_completed"]
|
||||||
|
|||||||
+107
-107
@@ -1,107 +1,107 @@
|
|||||||
extends AudioStreamPlayer
|
extends AudioStreamPlayer
|
||||||
|
|
||||||
const MENU_SCENES: Dictionary = {
|
const MENU_SCENES: Dictionary = {
|
||||||
"res://scenes/UI/start_screen.tscn": true,
|
"res://scenes/UI/start_screen.tscn": true,
|
||||||
"res://scenes/UI/Settings.tscn": true,
|
"res://scenes/UI/Settings.tscn": true,
|
||||||
"res://scenes/UI/login_screen.tscn": true,
|
"res://scenes/UI/login_screen.tscn": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
const CONFIG_PATH := "user://settings.cfg"
|
const CONFIG_PATH := "user://settings.cfg"
|
||||||
const CONFIG_SECTION := "audio"
|
const CONFIG_SECTION := "audio"
|
||||||
const CONFIG_KEY_MUSIC_VOLUME := "menu_music_volume"
|
const CONFIG_KEY_MUSIC_VOLUME := "menu_music_volume"
|
||||||
const CONFIG_KEY_MUSIC_MUTED := "menu_music_muted"
|
const CONFIG_KEY_MUSIC_MUTED := "menu_music_muted"
|
||||||
const DEFAULT_VOLUME := 0.7
|
const DEFAULT_VOLUME := 0.7
|
||||||
|
|
||||||
var _last_scene: Node = null
|
var _last_scene: Node = null
|
||||||
var _user_volume_linear: float = DEFAULT_VOLUME
|
var _user_volume_linear: float = DEFAULT_VOLUME
|
||||||
var _is_muted: bool = false
|
var _is_muted: bool = false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
set_process(true)
|
set_process(true)
|
||||||
_load_settings()
|
_load_settings()
|
||||||
_apply_volume()
|
_apply_volume()
|
||||||
_update_playback(get_tree().current_scene)
|
_update_playback(get_tree().current_scene)
|
||||||
|
|
||||||
func _process(_delta: float) -> void:
|
func _process(_delta: float) -> void:
|
||||||
var current := get_tree().current_scene
|
var current := get_tree().current_scene
|
||||||
if current != _last_scene:
|
if current != _last_scene:
|
||||||
_update_playback(current)
|
_update_playback(current)
|
||||||
elif _should_play_scene(current) and not playing and not _is_muted and _user_volume_linear > 0.0:
|
elif _should_play_scene(current) and not playing and not _is_muted and _user_volume_linear > 0.0:
|
||||||
play()
|
play()
|
||||||
|
|
||||||
func _update_playback(scene: Node) -> void:
|
func _update_playback(scene: Node) -> void:
|
||||||
if scene == null:
|
if scene == null:
|
||||||
_last_scene = null
|
_last_scene = null
|
||||||
return
|
return
|
||||||
_last_scene = scene
|
_last_scene = scene
|
||||||
if _should_play_scene(scene):
|
if _should_play_scene(scene):
|
||||||
if not playing:
|
if not playing:
|
||||||
play()
|
play()
|
||||||
elif playing:
|
elif playing:
|
||||||
stop()
|
stop()
|
||||||
|
|
||||||
func _should_play_scene(scene: Node) -> bool:
|
func _should_play_scene(scene: Node) -> bool:
|
||||||
if scene == null:
|
if scene == null:
|
||||||
return false
|
return false
|
||||||
var scene_path: String = scene.get_scene_file_path()
|
var scene_path: String = scene.get_scene_file_path()
|
||||||
if scene_path.is_empty():
|
if scene_path.is_empty():
|
||||||
return false
|
return false
|
||||||
return MENU_SCENES.has(scene_path)
|
return MENU_SCENES.has(scene_path)
|
||||||
|
|
||||||
func set_user_volume(value: float) -> void:
|
func set_user_volume(value: float) -> void:
|
||||||
var clamped_value: float = clamp(value, 0.0, 1.0)
|
var clamped_value: float = clamp(value, 0.0, 1.0)
|
||||||
if is_equal_approx(_user_volume_linear, clamped_value):
|
if is_equal_approx(_user_volume_linear, clamped_value):
|
||||||
return
|
return
|
||||||
_user_volume_linear = clamped_value
|
_user_volume_linear = clamped_value
|
||||||
_apply_volume()
|
_apply_volume()
|
||||||
_save_settings()
|
_save_settings()
|
||||||
|
|
||||||
func get_user_volume() -> float:
|
func get_user_volume() -> float:
|
||||||
return _user_volume_linear
|
return _user_volume_linear
|
||||||
|
|
||||||
func set_user_muted(muted: bool) -> void:
|
func set_user_muted(muted: bool) -> void:
|
||||||
var new_muted: bool = muted
|
var new_muted: bool = muted
|
||||||
if _is_muted == new_muted:
|
if _is_muted == new_muted:
|
||||||
return
|
return
|
||||||
_is_muted = new_muted
|
_is_muted = new_muted
|
||||||
_apply_volume()
|
_apply_volume()
|
||||||
_save_settings()
|
_save_settings()
|
||||||
|
|
||||||
func is_user_muted() -> bool:
|
func is_user_muted() -> bool:
|
||||||
return _is_muted
|
return _is_muted
|
||||||
|
|
||||||
func _apply_volume() -> void:
|
func _apply_volume() -> void:
|
||||||
if _is_muted or _user_volume_linear <= 0.0:
|
if _is_muted or _user_volume_linear <= 0.0:
|
||||||
volume_db = -80.0
|
volume_db = -80.0
|
||||||
else:
|
else:
|
||||||
volume_db = linear_to_db(_user_volume_linear)
|
volume_db = linear_to_db(_user_volume_linear)
|
||||||
|
|
||||||
func _load_settings() -> void:
|
func _load_settings() -> void:
|
||||||
var config: ConfigFile = ConfigFile.new()
|
var config: ConfigFile = ConfigFile.new()
|
||||||
var err: int = config.load(CONFIG_PATH)
|
var err: int = config.load(CONFIG_PATH)
|
||||||
if err == OK:
|
if err == OK:
|
||||||
var stored_volume: float = float(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_VOLUME, DEFAULT_VOLUME))
|
var stored_volume: float = float(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_VOLUME, DEFAULT_VOLUME))
|
||||||
_user_volume_linear = clamp(stored_volume, 0.0, 1.0)
|
_user_volume_linear = clamp(stored_volume, 0.0, 1.0)
|
||||||
var stored_muted: bool = bool(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_MUTED, false))
|
var stored_muted: bool = bool(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_MUTED, false))
|
||||||
_is_muted = stored_muted
|
_is_muted = stored_muted
|
||||||
elif err == ERR_DOES_NOT_EXIST:
|
elif err == ERR_DOES_NOT_EXIST:
|
||||||
_user_volume_linear = DEFAULT_VOLUME
|
_user_volume_linear = DEFAULT_VOLUME
|
||||||
_is_muted = false
|
_is_muted = false
|
||||||
else:
|
else:
|
||||||
push_warning("Failed to load settings.cfg: %s" % err)
|
push_warning("Failed to load settings.cfg: %s" % err)
|
||||||
_user_volume_linear = DEFAULT_VOLUME
|
_user_volume_linear = DEFAULT_VOLUME
|
||||||
_is_muted = false
|
_is_muted = false
|
||||||
|
|
||||||
func _save_settings() -> void:
|
func _save_settings() -> void:
|
||||||
var config: ConfigFile = ConfigFile.new()
|
var config: ConfigFile = ConfigFile.new()
|
||||||
var err: int = config.load(CONFIG_PATH)
|
var err: int = config.load(CONFIG_PATH)
|
||||||
if err != OK and err != ERR_DOES_NOT_EXIST:
|
if err != OK and err != ERR_DOES_NOT_EXIST:
|
||||||
push_warning("Failed to load settings.cfg before saving: %s" % err)
|
push_warning("Failed to load settings.cfg before saving: %s" % err)
|
||||||
config = ConfigFile.new()
|
config = ConfigFile.new()
|
||||||
elif err != OK:
|
elif err != OK:
|
||||||
config = ConfigFile.new()
|
config = ConfigFile.new()
|
||||||
config.set_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_VOLUME, _user_volume_linear)
|
config.set_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_VOLUME, _user_volume_linear)
|
||||||
config.set_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_MUTED, _is_muted)
|
config.set_value(CONFIG_SECTION, CONFIG_KEY_MUSIC_MUTED, _is_muted)
|
||||||
var save_err: int = config.save(CONFIG_PATH)
|
var save_err: int = config.save(CONFIG_PATH)
|
||||||
if save_err != OK:
|
if save_err != OK:
|
||||||
push_warning("Failed to save settings.cfg: %s" % save_err)
|
push_warning("Failed to save settings.cfg: %s" % save_err)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://l0cqi7dvoou3
|
uid://l0cqi7dvoou3
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://dfmq1n507y7d3"]
|
[gd_scene load_steps=3 format=3 uid="uid://dfmq1n507y7d3"]
|
||||||
|
|
||||||
[ext_resource type="AudioStream" uid="uid://txgki0ijeuud" path="res://assets/audio/silly-test.ogg" id="1_ek0t3"]
|
[ext_resource type="AudioStream" uid="uid://txgki0ijeuud" path="res://assets/audio/silly-test.ogg" id="1_ek0t3"]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://scenes/UI/menu_music.gd" id="2_21d4q"]
|
[ext_resource type="Script" path="res://scenes/UI/menu_music.gd" id="2_21d4q"]
|
||||||
|
|
||||||
[node name="MenuMusic" type="AudioStreamPlayer"]
|
[node name="MenuMusic" type="AudioStreamPlayer"]
|
||||||
bus = &"Music"
|
bus = &"Music"
|
||||||
stream = ExtResource("1_ek0t3")
|
stream = ExtResource("1_ek0t3")
|
||||||
autoplay = true
|
autoplay = true
|
||||||
volume_db = -3.0
|
volume_db = -3.0
|
||||||
priority = 10.0
|
priority = 10.0
|
||||||
script = ExtResource("2_21d4q")
|
script = ExtResource("2_21d4q")
|
||||||
|
|||||||
+76
-76
@@ -1,76 +1,76 @@
|
|||||||
extends AudioStreamPlayer
|
extends AudioStreamPlayer
|
||||||
|
|
||||||
const CONFIG_PATH := "user://settings.cfg"
|
const CONFIG_PATH := "user://settings.cfg"
|
||||||
const CONFIG_SECTION := "audio"
|
const CONFIG_SECTION := "audio"
|
||||||
const CONFIG_KEY_VOLUME := "menu_sfx_volume"
|
const CONFIG_KEY_VOLUME := "menu_sfx_volume"
|
||||||
const CONFIG_KEY_MUTED := "menu_sfx_muted"
|
const CONFIG_KEY_MUTED := "menu_sfx_muted"
|
||||||
const DEFAULT_VOLUME := 0.7
|
const DEFAULT_VOLUME := 0.7
|
||||||
|
|
||||||
var _user_volume_linear: float = DEFAULT_VOLUME
|
var _user_volume_linear: float = DEFAULT_VOLUME
|
||||||
var _is_muted: bool = false
|
var _is_muted: bool = false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
_load_settings()
|
_load_settings()
|
||||||
_apply_volume()
|
_apply_volume()
|
||||||
|
|
||||||
func play_hover() -> void:
|
func play_hover() -> void:
|
||||||
if stream == null or _is_muted or _user_volume_linear <= 0.0:
|
if stream == null or _is_muted or _user_volume_linear <= 0.0:
|
||||||
return
|
return
|
||||||
play(0.0)
|
play(0.0)
|
||||||
|
|
||||||
func set_user_volume(value: float) -> void:
|
func set_user_volume(value: float) -> void:
|
||||||
var clamped_value: float = clamp(value, 0.0, 1.0)
|
var clamped_value: float = clamp(value, 0.0, 1.0)
|
||||||
if is_equal_approx(_user_volume_linear, clamped_value):
|
if is_equal_approx(_user_volume_linear, clamped_value):
|
||||||
return
|
return
|
||||||
_user_volume_linear = clamped_value
|
_user_volume_linear = clamped_value
|
||||||
_apply_volume()
|
_apply_volume()
|
||||||
_save_settings()
|
_save_settings()
|
||||||
|
|
||||||
func get_user_volume() -> float:
|
func get_user_volume() -> float:
|
||||||
return _user_volume_linear
|
return _user_volume_linear
|
||||||
|
|
||||||
func set_user_muted(muted: bool) -> void:
|
func set_user_muted(muted: bool) -> void:
|
||||||
if _is_muted == muted:
|
if _is_muted == muted:
|
||||||
return
|
return
|
||||||
_is_muted = muted
|
_is_muted = muted
|
||||||
_apply_volume()
|
_apply_volume()
|
||||||
_save_settings()
|
_save_settings()
|
||||||
if _is_muted:
|
if _is_muted:
|
||||||
stop()
|
stop()
|
||||||
|
|
||||||
func is_user_muted() -> bool:
|
func is_user_muted() -> bool:
|
||||||
return _is_muted
|
return _is_muted
|
||||||
|
|
||||||
func _apply_volume() -> void:
|
func _apply_volume() -> void:
|
||||||
if _is_muted or _user_volume_linear <= 0.0:
|
if _is_muted or _user_volume_linear <= 0.0:
|
||||||
volume_db = -80.0
|
volume_db = -80.0
|
||||||
else:
|
else:
|
||||||
volume_db = linear_to_db(_user_volume_linear)
|
volume_db = linear_to_db(_user_volume_linear)
|
||||||
|
|
||||||
func _load_settings() -> void:
|
func _load_settings() -> void:
|
||||||
var config: ConfigFile = ConfigFile.new()
|
var config: ConfigFile = ConfigFile.new()
|
||||||
var err: int = config.load(CONFIG_PATH)
|
var err: int = config.load(CONFIG_PATH)
|
||||||
if err == OK:
|
if err == OK:
|
||||||
_user_volume_linear = clamp(float(config.get_value(CONFIG_SECTION, CONFIG_KEY_VOLUME, DEFAULT_VOLUME)), 0.0, 1.0)
|
_user_volume_linear = clamp(float(config.get_value(CONFIG_SECTION, CONFIG_KEY_VOLUME, DEFAULT_VOLUME)), 0.0, 1.0)
|
||||||
_is_muted = bool(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUTED, false))
|
_is_muted = bool(config.get_value(CONFIG_SECTION, CONFIG_KEY_MUTED, false))
|
||||||
elif err == ERR_DOES_NOT_EXIST:
|
elif err == ERR_DOES_NOT_EXIST:
|
||||||
_user_volume_linear = DEFAULT_VOLUME
|
_user_volume_linear = DEFAULT_VOLUME
|
||||||
_is_muted = false
|
_is_muted = false
|
||||||
else:
|
else:
|
||||||
push_warning("Failed to load settings.cfg: %s" % err)
|
push_warning("Failed to load settings.cfg: %s" % err)
|
||||||
_user_volume_linear = DEFAULT_VOLUME
|
_user_volume_linear = DEFAULT_VOLUME
|
||||||
_is_muted = false
|
_is_muted = false
|
||||||
|
|
||||||
func _save_settings() -> void:
|
func _save_settings() -> void:
|
||||||
var config: ConfigFile = ConfigFile.new()
|
var config: ConfigFile = ConfigFile.new()
|
||||||
var err: int = config.load(CONFIG_PATH)
|
var err: int = config.load(CONFIG_PATH)
|
||||||
if err != OK and err != ERR_DOES_NOT_EXIST:
|
if err != OK and err != ERR_DOES_NOT_EXIST:
|
||||||
push_warning("Failed to load settings.cfg before saving: %s" % err)
|
push_warning("Failed to load settings.cfg before saving: %s" % err)
|
||||||
config = ConfigFile.new()
|
config = ConfigFile.new()
|
||||||
elif err != OK:
|
elif err != OK:
|
||||||
config = ConfigFile.new()
|
config = ConfigFile.new()
|
||||||
config.set_value(CONFIG_SECTION, CONFIG_KEY_VOLUME, _user_volume_linear)
|
config.set_value(CONFIG_SECTION, CONFIG_KEY_VOLUME, _user_volume_linear)
|
||||||
config.set_value(CONFIG_SECTION, CONFIG_KEY_MUTED, _is_muted)
|
config.set_value(CONFIG_SECTION, CONFIG_KEY_MUTED, _is_muted)
|
||||||
var save_err: int = config.save(CONFIG_PATH)
|
var save_err: int = config.save(CONFIG_PATH)
|
||||||
if save_err != OK:
|
if save_err != OK:
|
||||||
push_warning("Failed to save settings.cfg: %s" % save_err)
|
push_warning("Failed to save settings.cfg: %s" % save_err)
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://c7ixr4hbh5ad6
|
uid://c7ixr4hbh5ad6
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
[gd_scene load_steps=3 format=3 uid="uid://dt785sv7ie7uj"]
|
[gd_scene load_steps=3 format=3 uid="uid://dt785sv7ie7uj"]
|
||||||
|
|
||||||
[ext_resource type="AudioStream" uid="uid://64dplcgx2icb" path="res://assets/audio/silly-menu-hover-test.ogg" id="1_a5j5k"]
|
[ext_resource type="AudioStream" uid="uid://64dplcgx2icb" path="res://assets/audio/silly-menu-hover-test.ogg" id="1_a5j5k"]
|
||||||
[ext_resource type="Script" path="res://scenes/UI/menu_sfx.gd" id="1_ijvfa"]
|
[ext_resource type="Script" path="res://scenes/UI/menu_sfx.gd" id="1_ijvfa"]
|
||||||
|
|
||||||
[node name="MenuSfx" type="AudioStreamPlayer"]
|
[node name="MenuSfx" type="AudioStreamPlayer"]
|
||||||
bus = &"SFX"
|
bus = &"SFX"
|
||||||
stream = ExtResource("1_a5j5k")
|
stream = ExtResource("1_a5j5k")
|
||||||
volume_db = -6.0
|
volume_db = -6.0
|
||||||
autoplay = false
|
autoplay = false
|
||||||
priority = 0.5
|
priority = 0.5
|
||||||
max_polyphony = 4
|
max_polyphony = 4
|
||||||
script = ExtResource("1_ijvfa")
|
script = ExtResource("1_ijvfa")
|
||||||
|
|||||||
@@ -0,0 +1,16 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
var character: Dictionary = {}
|
||||||
|
|
||||||
|
func set_character(data: Dictionary) -> void:
|
||||||
|
character = data
|
||||||
|
|
||||||
|
func clear() -> void:
|
||||||
|
character = {}
|
||||||
|
|
||||||
|
func get_coord() -> Vector2:
|
||||||
|
var coord: Dictionary = character.get("coord", {})
|
||||||
|
return Vector2(
|
||||||
|
float(coord.get("x", 0)),
|
||||||
|
float(coord.get("y", 0))
|
||||||
|
)
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://bbl0our5y4k7a
|
||||||
@@ -1,66 +1,66 @@
|
|||||||
extends Control
|
extends Control
|
||||||
|
|
||||||
const AUTH_LOGOUT_URL := "https://pauth.ranaze.com/api/Auth/logout"
|
const AUTH_LOGOUT_URL := "https://pauth.ranaze.com/api/Auth/logout"
|
||||||
|
|
||||||
@onready var _login_button: Button = $MarginContainer/CenterContainer/ContentVBox/VBoxContainer/LogInButton
|
@onready var _login_button: Button = $MarginContainer/CenterContainer/ContentVBox/VBoxContainer/LogInButton
|
||||||
@onready var _logout_request: HTTPRequest = %LogoutRequest
|
@onready var _logout_request: HTTPRequest = %LogoutRequest
|
||||||
|
|
||||||
func _ready():
|
func _ready():
|
||||||
_register_focus_sounds()
|
_register_focus_sounds()
|
||||||
_update_login_button()
|
_update_login_button()
|
||||||
|
|
||||||
func _register_focus_sounds() -> void:
|
func _register_focus_sounds() -> void:
|
||||||
var button_container := $MarginContainer/CenterContainer/ContentVBox/VBoxContainer
|
var button_container := $MarginContainer/CenterContainer/ContentVBox/VBoxContainer
|
||||||
for child in button_container.get_children():
|
for child in button_container.get_children():
|
||||||
if child is BaseButton:
|
if child is BaseButton:
|
||||||
var button: BaseButton = child
|
var button: BaseButton = child
|
||||||
if not button.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")):
|
if not button.is_connected("focus_entered", Callable(self, "_on_menu_item_focus")):
|
||||||
button.focus_entered.connect(_on_menu_item_focus)
|
button.focus_entered.connect(_on_menu_item_focus)
|
||||||
if not button.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")):
|
if not button.is_connected("mouse_entered", Callable(self, "_on_menu_item_focus")):
|
||||||
button.mouse_entered.connect(_on_menu_item_focus)
|
button.mouse_entered.connect(_on_menu_item_focus)
|
||||||
|
|
||||||
func _on_start_button_pressed():
|
func _on_start_button_pressed():
|
||||||
get_tree().change_scene_to_file("uid://dchj6g2i8ebph")
|
get_tree().change_scene_to_file("uid://dchj6g2i8ebph")
|
||||||
|
|
||||||
func _on_settings_button_pressed():
|
func _on_settings_button_pressed():
|
||||||
get_tree().change_scene_to_file("uid://d3tqrm4ry4l88")
|
get_tree().change_scene_to_file("uid://d3tqrm4ry4l88")
|
||||||
|
|
||||||
func _on_quit_button_pressed():
|
func _on_quit_button_pressed():
|
||||||
get_tree().quit()
|
get_tree().quit()
|
||||||
|
|
||||||
func _on_log_in_button_pressed():
|
func _on_log_in_button_pressed():
|
||||||
if AuthState.is_logged_in:
|
if AuthState.is_logged_in:
|
||||||
_request_logout()
|
_request_logout()
|
||||||
else:
|
else:
|
||||||
get_tree().change_scene_to_file("res://scenes/UI/login_screen.tscn")
|
get_tree().change_scene_to_file("res://scenes/UI/login_screen.tscn")
|
||||||
|
|
||||||
func _on_menu_item_focus() -> void:
|
func _on_menu_item_focus() -> void:
|
||||||
if MenuSfx:
|
if MenuSfx:
|
||||||
MenuSfx.play_hover()
|
MenuSfx.play_hover()
|
||||||
|
|
||||||
func _request_logout() -> void:
|
func _request_logout() -> void:
|
||||||
if AuthState.access_token.is_empty():
|
if AuthState.access_token.is_empty():
|
||||||
AuthState.clear_session()
|
AuthState.clear_session()
|
||||||
_update_login_button()
|
_update_login_button()
|
||||||
return
|
return
|
||||||
var headers := PackedStringArray([
|
var headers := PackedStringArray([
|
||||||
"Authorization: Bearer %s" % AuthState.access_token,
|
"Authorization: Bearer %s" % AuthState.access_token,
|
||||||
])
|
])
|
||||||
var err := _logout_request.request(AUTH_LOGOUT_URL, headers, HTTPClient.METHOD_POST)
|
var err := _logout_request.request(AUTH_LOGOUT_URL, headers, HTTPClient.METHOD_POST)
|
||||||
if err != OK:
|
if err != OK:
|
||||||
push_warning("Failed to send logout request: %s" % err)
|
push_warning("Failed to send logout request: %s" % err)
|
||||||
AuthState.clear_session()
|
AuthState.clear_session()
|
||||||
_update_login_button()
|
_update_login_button()
|
||||||
|
|
||||||
func _on_logout_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
func _on_logout_request_completed(result: int, response_code: int, _headers: PackedStringArray, body: PackedByteArray) -> void:
|
||||||
var body_text := body.get_string_from_utf8()
|
var body_text := body.get_string_from_utf8()
|
||||||
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
if result != HTTPRequest.RESULT_SUCCESS or response_code < 200 or response_code >= 300:
|
||||||
push_warning("Logout failed (%s/%s): %s" % [result, response_code, body_text])
|
push_warning("Logout failed (%s/%s): %s" % [result, response_code, body_text])
|
||||||
AuthState.clear_session()
|
AuthState.clear_session()
|
||||||
_update_login_button()
|
_update_login_button()
|
||||||
|
|
||||||
func _update_login_button() -> void:
|
func _update_login_button() -> void:
|
||||||
if AuthState.is_logged_in:
|
if AuthState.is_logged_in:
|
||||||
_login_button.text = "LOG OUT"
|
_login_button.text = "LOG OUT"
|
||||||
else:
|
else:
|
||||||
_login_button.text = "LOG IN"
|
_login_button.text = "LOG IN"
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://cc8lskf7y74kh
|
uid://cc8lskf7y74kh
|
||||||
|
|||||||
+100
-100
@@ -1,100 +1,100 @@
|
|||||||
[gd_scene load_steps=5 format=3 uid="uid://b4k81taauef4q"]
|
[gd_scene load_steps=5 format=3 uid="uid://b4k81taauef4q"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://cc8lskf7y74kh" path="res://scenes/UI/start_screen.gd" id="1_o7i0r"]
|
[ext_resource type="Script" uid="uid://cc8lskf7y74kh" path="res://scenes/UI/start_screen.gd" id="1_o7i0r"]
|
||||||
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="1_tx5wa"]
|
[ext_resource type="Theme" uid="uid://wpxmub0n2dr3" path="res://themes/button_theme.tres" id="1_tx5wa"]
|
||||||
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="2_r2jwc"]
|
[ext_resource type="Texture2D" uid="uid://dhuosr0p605gj" path="res://assets/images/pp_start_bg.png" id="2_r2jwc"]
|
||||||
[ext_resource type="Theme" uid="uid://tn8qndst18d6" path="res://themes/title_font_theme.tres" id="4_hm208"]
|
[ext_resource type="Theme" uid="uid://tn8qndst18d6" path="res://themes/title_font_theme.tres" id="4_hm208"]
|
||||||
|
|
||||||
[node name="StartScreen" type="Control"]
|
[node name="StartScreen" type="Control"]
|
||||||
layout_mode = 3
|
layout_mode = 3
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
script = ExtResource("1_o7i0r")
|
script = ExtResource("1_o7i0r")
|
||||||
|
|
||||||
[node name="TextureRect" type="TextureRect" parent="."]
|
[node name="TextureRect" type="TextureRect" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
texture = ExtResource("2_r2jwc")
|
texture = ExtResource("2_r2jwc")
|
||||||
|
|
||||||
[node name="MarginContainer" type="MarginContainer" parent="."]
|
[node name="MarginContainer" type="MarginContainer" parent="."]
|
||||||
layout_mode = 1
|
layout_mode = 1
|
||||||
anchors_preset = 15
|
anchors_preset = 15
|
||||||
anchor_right = 1.0
|
anchor_right = 1.0
|
||||||
anchor_bottom = 1.0
|
anchor_bottom = 1.0
|
||||||
grow_horizontal = 2
|
grow_horizontal = 2
|
||||||
grow_vertical = 2
|
grow_vertical = 2
|
||||||
theme_override_constants/margin_left = 10
|
theme_override_constants/margin_left = 10
|
||||||
theme_override_constants/margin_top = 10
|
theme_override_constants/margin_top = 10
|
||||||
theme_override_constants/margin_right = 10
|
theme_override_constants/margin_right = 10
|
||||||
theme_override_constants/margin_bottom = 10
|
theme_override_constants/margin_bottom = 10
|
||||||
|
|
||||||
[node name="CenterContainer" type="CenterContainer" parent="MarginContainer"]
|
[node name="CenterContainer" type="CenterContainer" parent="MarginContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="ContentVBox" type="VBoxContainer" parent="MarginContainer/CenterContainer"]
|
[node name="ContentVBox" type="VBoxContainer" parent="MarginContainer/CenterContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 8
|
size_flags_vertical = 8
|
||||||
theme_override_constants/separation = 10
|
theme_override_constants/separation = 10
|
||||||
|
|
||||||
[node name="Label" type="Label" parent="MarginContainer/CenterContainer/ContentVBox"]
|
[node name="Label" type="Label" parent="MarginContainer/CenterContainer/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
theme = ExtResource("4_hm208")
|
theme = ExtResource("4_hm208")
|
||||||
text = "PROJECT
|
text = "PROJECT
|
||||||
PROMISCUOUS"
|
PROMISCUOUS"
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="TitleSpacer" type="Control" parent="MarginContainer/CenterContainer/ContentVBox"]
|
[node name="TitleSpacer" type="Control" parent="MarginContainer/CenterContainer/ContentVBox"]
|
||||||
custom_minimum_size = Vector2(0, 40)
|
custom_minimum_size = Vector2(0, 40)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/CenterContainer/ContentVBox"]
|
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/CenterContainer/ContentVBox"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 8
|
size_flags_vertical = 8
|
||||||
theme_override_constants/separation = 6
|
theme_override_constants/separation = 6
|
||||||
|
|
||||||
[node name="LogInButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
[node name="LogInButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
theme = ExtResource("1_tx5wa")
|
theme = ExtResource("1_tx5wa")
|
||||||
text = "LOG IN"
|
text = "LOG IN"
|
||||||
|
|
||||||
[node name="StartButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
[node name="StartButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
theme = ExtResource("1_tx5wa")
|
theme = ExtResource("1_tx5wa")
|
||||||
text = "START"
|
text = "START"
|
||||||
|
|
||||||
[node name="SettingsButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
[node name="SettingsButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
theme = ExtResource("1_tx5wa")
|
theme = ExtResource("1_tx5wa")
|
||||||
text = "SETTINGS"
|
text = "SETTINGS"
|
||||||
|
|
||||||
[node name="QuitButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
[node name="QuitButton" type="Button" parent="MarginContainer/CenterContainer/ContentVBox/VBoxContainer"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_horizontal = 4
|
size_flags_horizontal = 4
|
||||||
size_flags_vertical = 4
|
size_flags_vertical = 4
|
||||||
theme = ExtResource("1_tx5wa")
|
theme = ExtResource("1_tx5wa")
|
||||||
text = "QUIT"
|
text = "QUIT"
|
||||||
|
|
||||||
[node name="LogoutRequest" type="HTTPRequest" parent="."]
|
[node name="LogoutRequest" type="HTTPRequest" parent="."]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
|
|
||||||
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/LogInButton" to="." method="_on_log_in_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/LogInButton" to="." method="_on_log_in_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/StartButton" to="." method="_on_start_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/StartButton" to="." method="_on_start_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/SettingsButton" to="." method="_on_settings_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/SettingsButton" to="." method="_on_settings_button_pressed"]
|
||||||
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/QuitButton" to="." method="_on_quit_button_pressed"]
|
[connection signal="pressed" from="MarginContainer/CenterContainer/ContentVBox/VBoxContainer/QuitButton" to="." method="_on_quit_button_pressed"]
|
||||||
[connection signal="request_completed" from="LogoutRequest" to="." method="_on_logout_request_completed"]
|
[connection signal="request_completed" from="LogoutRequest" to="." method="_on_logout_request_completed"]
|
||||||
|
|||||||
@@ -0,0 +1,105 @@
|
|||||||
|
extends RigidBody3D
|
||||||
|
|
||||||
|
@export var drive_speed := 18.0
|
||||||
|
@export var drive_accel := 20.0
|
||||||
|
@export var brake_strength := 28.0
|
||||||
|
@export var turn_speed := 2.0
|
||||||
|
@export var turn_accel := 8.0
|
||||||
|
@export var lateral_damp := 10.0
|
||||||
|
@export var launch_impulse := 28.0
|
||||||
|
@export var launch_up_impulse := 6.0
|
||||||
|
@export var seat_path: NodePath
|
||||||
|
@export var exit_path: NodePath
|
||||||
|
@export var camera_path: NodePath
|
||||||
|
@export var interact_area_path: NodePath
|
||||||
|
|
||||||
|
@onready var seat: Node3D = get_node(seat_path) if seat_path != NodePath("") else null
|
||||||
|
@onready var exit_point: Node3D = get_node(exit_path) if exit_path != NodePath("") else null
|
||||||
|
@onready var car_camera: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null
|
||||||
|
@onready var interact_area: Area3D = get_node(interact_area_path) if interact_area_path != NodePath("") else null
|
||||||
|
|
||||||
|
var _nearby_driver: Node = null
|
||||||
|
var _driver: Node = null
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
if interact_area:
|
||||||
|
interact_area.collision_layer = 2
|
||||||
|
interact_area.collision_mask = 1
|
||||||
|
interact_area.body_entered.connect(_on_interact_body_entered)
|
||||||
|
interact_area.body_exited.connect(_on_interact_body_exited)
|
||||||
|
if car_camera:
|
||||||
|
car_camera.current = false
|
||||||
|
contact_monitor = true
|
||||||
|
max_contacts_reported = 8
|
||||||
|
|
||||||
|
func _process(_delta: float) -> void:
|
||||||
|
if _driver == null and _nearby_driver != null and Input.is_action_just_pressed("interact"):
|
||||||
|
_enter_vehicle(_nearby_driver)
|
||||||
|
elif _driver != null and Input.is_action_just_pressed("interact"):
|
||||||
|
_exit_vehicle()
|
||||||
|
|
||||||
|
func _integrate_forces(state: PhysicsDirectBodyState3D) -> void:
|
||||||
|
if _driver == null:
|
||||||
|
return
|
||||||
|
|
||||||
|
var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
|
||||||
|
var forward := global_transform.basis.z
|
||||||
|
forward.y = 0.0
|
||||||
|
forward = forward.normalized()
|
||||||
|
|
||||||
|
var current_speed := linear_velocity.dot(forward)
|
||||||
|
var target_speed := input2v.y * drive_speed
|
||||||
|
var accel := drive_accel if abs(input2v.y) > 0.01 else brake_strength
|
||||||
|
var new_forward_speed := move_toward(current_speed, target_speed, accel * state.step)
|
||||||
|
var forward_vel := forward * new_forward_speed
|
||||||
|
var lateral_vel := linear_velocity - (forward * current_speed)
|
||||||
|
lateral_vel = lateral_vel.move_toward(Vector3.ZERO, lateral_damp * state.step)
|
||||||
|
linear_velocity = forward_vel + lateral_vel
|
||||||
|
|
||||||
|
var speed_factor: float = clamp(abs(new_forward_speed) / drive_speed, 0.0, 1.0)
|
||||||
|
var turn_input := input2v.x
|
||||||
|
if new_forward_speed < -0.1:
|
||||||
|
turn_input = -turn_input
|
||||||
|
var target_turn: float = turn_input * turn_speed * speed_factor
|
||||||
|
angular_velocity.y = move_toward(angular_velocity.y, target_turn, turn_accel * state.step)
|
||||||
|
|
||||||
|
if speed_factor > 0.2:
|
||||||
|
var hit_ids := {}
|
||||||
|
for i in state.get_contact_count():
|
||||||
|
var collider := state.get_contact_collider_object(i)
|
||||||
|
if collider is RigidBody3D and collider != self:
|
||||||
|
var collider_id := (collider as Node).get_instance_id()
|
||||||
|
if hit_ids.has(collider_id):
|
||||||
|
continue
|
||||||
|
hit_ids[collider_id] = true
|
||||||
|
var contact_pos := state.get_contact_collider_position(i)
|
||||||
|
var launch_dir := (contact_pos - global_position).normalized()
|
||||||
|
if launch_dir.length() <= 0.001:
|
||||||
|
var normal_world := (global_transform.basis * state.get_contact_local_normal(i)).normalized()
|
||||||
|
launch_dir = normal_world if normal_world.length() > 0.001 else forward.normalized()
|
||||||
|
var impulse := launch_dir * (launch_impulse * speed_factor) + Vector3.UP * launch_up_impulse
|
||||||
|
(collider as RigidBody3D).apply_central_impulse(impulse)
|
||||||
|
|
||||||
|
func _enter_vehicle(player: Node) -> void:
|
||||||
|
if seat == null:
|
||||||
|
return
|
||||||
|
_driver = player
|
||||||
|
player.call("enter_vehicle", self, seat, car_camera)
|
||||||
|
if car_camera:
|
||||||
|
car_camera.current = true
|
||||||
|
|
||||||
|
func _exit_vehicle() -> void:
|
||||||
|
if _driver == null:
|
||||||
|
return
|
||||||
|
_driver.call("exit_vehicle", exit_point, car_camera)
|
||||||
|
_driver = null
|
||||||
|
if car_camera:
|
||||||
|
car_camera.current = false
|
||||||
|
|
||||||
|
func _on_interact_body_entered(body: Node) -> void:
|
||||||
|
if body.has_method("enter_vehicle"):
|
||||||
|
_nearby_driver = body
|
||||||
|
|
||||||
|
func _on_interact_body_exited(body: Node) -> void:
|
||||||
|
if body == _nearby_driver:
|
||||||
|
_nearby_driver = null
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://4qf5yinepytc
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
[gd_scene load_steps=6 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://scenes/Vehicles/car.gd" id="1_kbd20"]
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_7r1j6"]
|
||||||
|
size = Vector3(1.4, 0.9, 2.6)
|
||||||
|
|
||||||
|
[sub_resource type="BoxShape3D" id="BoxShape3D_jk0m1"]
|
||||||
|
size = Vector3(2.2, 2.0, 3.8)
|
||||||
|
|
||||||
|
[sub_resource type="BoxMesh" id="BoxMesh_4y8xk"]
|
||||||
|
size = Vector3(1.4, 0.9, 2.6)
|
||||||
|
|
||||||
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_red"]
|
||||||
|
albedo_color = Color(0.85, 0.1, 0.1, 1)
|
||||||
|
|
||||||
|
[node name="Car" type="RigidBody3D"]
|
||||||
|
script = ExtResource("1_kbd20")
|
||||||
|
seat_path = NodePath("Seat")
|
||||||
|
exit_path = NodePath("Exit")
|
||||||
|
camera_path = NodePath("CarCamera")
|
||||||
|
interact_area_path = NodePath("InteractArea")
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
|
||||||
|
shape = SubResource("BoxShape3D_7r1j6")
|
||||||
|
|
||||||
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
|
||||||
|
mesh = SubResource("BoxMesh_4y8xk")
|
||||||
|
surface_material_override/0 = SubResource("StandardMaterial3D_red")
|
||||||
|
|
||||||
|
[node name="InteractArea" type="Area3D" parent="."]
|
||||||
|
|
||||||
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="InteractArea"]
|
||||||
|
shape = SubResource("BoxShape3D_jk0m1")
|
||||||
|
|
||||||
|
[node name="Seat" type="Node3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0, 0.6, 0.0)
|
||||||
|
|
||||||
|
[node name="Exit" type="Node3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1.3, 0.0, 0.0)
|
||||||
|
|
||||||
|
[node name="CarCamera" type="Camera3D" parent="."]
|
||||||
|
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.0, 2.0, 4.2)
|
||||||
+21
-21
@@ -1,21 +1,21 @@
|
|||||||
[gd_scene load_steps=4 format=3 uid="uid://c5of6aaxop1hl"]
|
[gd_scene load_steps=4 format=3 uid="uid://c5of6aaxop1hl"]
|
||||||
|
|
||||||
[sub_resource type="BoxShape3D" id="BoxShape3D_4du60"]
|
[sub_resource type="BoxShape3D" id="BoxShape3D_4du60"]
|
||||||
|
|
||||||
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_alp5v"]
|
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_alp5v"]
|
||||||
albedo_color = Color(0.290196, 0.698039, 0.227451, 1)
|
albedo_color = Color(0.290196, 0.698039, 0.227451, 1)
|
||||||
|
|
||||||
[sub_resource type="BoxMesh" id="BoxMesh_kryjk"]
|
[sub_resource type="BoxMesh" id="BoxMesh_kryjk"]
|
||||||
material = SubResource("StandardMaterial3D_alp5v")
|
material = SubResource("StandardMaterial3D_alp5v")
|
||||||
|
|
||||||
[node name="Block" type="Node3D"]
|
[node name="Block" type="Node3D"]
|
||||||
|
|
||||||
[node name="RigidBody3D" type="RigidBody3D" parent="."]
|
[node name="RigidBody3D" type="RigidBody3D" parent="."]
|
||||||
collision_layer = 3
|
collision_layer = 3
|
||||||
collision_mask = 3
|
collision_mask = 3
|
||||||
|
|
||||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D"]
|
[node name="CollisionShape3D" type="CollisionShape3D" parent="RigidBody3D"]
|
||||||
shape = SubResource("BoxShape3D_4du60")
|
shape = SubResource("BoxShape3D_4du60")
|
||||||
|
|
||||||
[node name="MeshInstance3D" type="MeshInstance3D" parent="RigidBody3D"]
|
[node name="MeshInstance3D" type="MeshInstance3D" parent="RigidBody3D"]
|
||||||
mesh = SubResource("BoxMesh_kryjk")
|
mesh = SubResource("BoxMesh_kryjk")
|
||||||
|
|||||||
+202
-112
@@ -1,114 +1,141 @@
|
|||||||
extends RigidBody3D
|
extends RigidBody3D
|
||||||
# Initially I used a CharacterBody3D, however, I wanted the player to bounce off
|
# Initially I used a CharacterBody3D, however, I wanted the player to bounce off
|
||||||
# other objects in the environment and that would have required manual handling
|
# other objects in the environment and that would have required manual handling
|
||||||
# of collisions. So that's why we're using a RigidBody3D instead.
|
# of collisions. So that's why we're using a RigidBody3D instead.
|
||||||
|
|
||||||
const MOVE_SPEED := 8.0
|
const MOVE_SPEED := 8.0
|
||||||
const ACCELLERATION := 30.0
|
const ACCELLERATION := 30.0
|
||||||
const DECELLERATION := 40.0
|
const DECELLERATION := 40.0
|
||||||
const JUMP_SPEED := 4.0
|
const JUMP_SPEED := 4.0
|
||||||
const MAX_NUMBER_OF_JUMPS := 2
|
const MAX_NUMBER_OF_JUMPS := 2
|
||||||
|
|
||||||
const MIN_FOV := 10
|
const MIN_FOV := 10
|
||||||
const MAX_FOV := 180
|
const MAX_FOV := 180
|
||||||
const ZOOM_FACTOR := 1.1 # Zoom out when >1, in when < 1
|
const ZOOM_FACTOR := 1.1 # Zoom out when >1, in when < 1
|
||||||
var mouse_sensitivity := 0.005
|
var mouse_sensitivity := 0.005
|
||||||
var rotation_x := 0.0
|
var rotation_x := 0.0
|
||||||
var rotation_y := 0.0
|
var rotation_y := 0.0
|
||||||
var cameraMoveMode := false
|
var cameraMoveMode := false
|
||||||
var current_number_of_jumps := 0
|
var current_number_of_jumps := 0
|
||||||
var _pending_mouse_delta := Vector2.ZERO
|
var _pending_mouse_delta := Vector2.ZERO
|
||||||
var _last_move_forward := Vector3(0, 0, 1)
|
var _last_move_forward := Vector3(0, 0, 1)
|
||||||
var _last_move_right := Vector3(1, 0, 0)
|
var _last_move_right := Vector3(1, 0, 0)
|
||||||
var _camera_offset_local := Vector3.ZERO
|
var _camera_offset_local := Vector3.ZERO
|
||||||
var _camera_yaw := 0.0
|
var _camera_yaw := 0.0
|
||||||
var _camera_pitch := 0.0
|
var _camera_pitch := 0.0
|
||||||
|
var _in_vehicle := false
|
||||||
|
var _vehicle_collision_layer := 0
|
||||||
|
var _vehicle_collision_mask := 0
|
||||||
|
var _vehicle_original_parent: Node = null
|
||||||
|
var _light_was_on := false
|
||||||
|
var _jump_triggered := false
|
||||||
|
@onready var _flashlight: SpotLight3D = $SpotLight3D
|
||||||
|
@onready var _anim_player: AnimationPlayer = find_child("AnimationPlayer", true, false) as AnimationPlayer
|
||||||
|
@onready var _model_root: Node3D = find_child("TestCharAnimated", true, false) as Node3D
|
||||||
|
|
||||||
@export var camera_follow_speed := 10.0
|
@export var camera_follow_speed := 10.0
|
||||||
|
@export var anim_idle_name := "Idle"
|
||||||
|
@export var anim_walk_name := "Walk"
|
||||||
|
@export var anim_jump_name := "Jump"
|
||||||
|
@export var anim_walk_speed_threshold := 0.25
|
||||||
|
|
||||||
var jump_sound = preload("res://assets/audio/jump.ogg")
|
var jump_sound = preload("res://assets/audio/jump.ogg")
|
||||||
var audio_player = AudioStreamPlayer.new()
|
var audio_player = AudioStreamPlayer.new()
|
||||||
|
|
||||||
@export var camera_path: NodePath
|
@export var camera_path: NodePath
|
||||||
@onready var cam: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null
|
@onready var cam: Camera3D = get_node(camera_path) if camera_path != NodePath("") else null
|
||||||
|
@export var phone_path: NodePath
|
||||||
|
@onready var phone: CanvasLayer = get_node(phone_path) if phone_path != NodePath("") else null
|
||||||
|
var phone_visible := false
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
axis_lock_angular_x = true
|
axis_lock_angular_x = true
|
||||||
axis_lock_angular_z = true
|
axis_lock_angular_z = true
|
||||||
angular_damp = 6.0
|
angular_damp = 6.0
|
||||||
contact_monitor = true
|
contact_monitor = true
|
||||||
max_contacts_reported = 4
|
max_contacts_reported = 4
|
||||||
add_child(audio_player)
|
add_child(audio_player)
|
||||||
audio_player.stream = jump_sound
|
audio_player.stream = jump_sound
|
||||||
audio_player.volume_db = -20
|
audio_player.volume_db = -20
|
||||||
if cam:
|
if cam:
|
||||||
_camera_offset_local = cam.transform.origin
|
_camera_offset_local = cam.transform.origin
|
||||||
_camera_pitch = cam.rotation.x
|
_camera_pitch = cam.rotation.x
|
||||||
_camera_yaw = global_transform.basis.get_euler().y
|
_camera_yaw = global_transform.basis.get_euler().y
|
||||||
cam.set_as_top_level(true)
|
cam.set_as_top_level(true)
|
||||||
cam.global_position = global_position + (Basis(Vector3.UP, _camera_yaw) * _camera_offset_local)
|
cam.global_position = global_position + (Basis(Vector3.UP, _camera_yaw) * _camera_offset_local)
|
||||||
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0)
|
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0)
|
||||||
var move_basis := cam.global_transform.basis if cam else global_transform.basis
|
var move_basis := cam.global_transform.basis if cam else global_transform.basis
|
||||||
var forward := move_basis.z
|
var forward := move_basis.z
|
||||||
var right := move_basis.x
|
var right := move_basis.x
|
||||||
forward.y = 0.0
|
forward.y = 0.0
|
||||||
right.y = 0.0
|
right.y = 0.0
|
||||||
if forward.length() > 0.0001:
|
if forward.length() > 0.0001:
|
||||||
_last_move_forward = forward.normalized()
|
_last_move_forward = forward.normalized()
|
||||||
if right.length() > 0.0001:
|
if right.length() > 0.0001:
|
||||||
_last_move_right = right.normalized()
|
_last_move_right = right.normalized()
|
||||||
|
_vehicle_collision_layer = collision_layer
|
||||||
|
_vehicle_collision_mask = collision_mask
|
||||||
|
|
||||||
func _integrate_forces(state):
|
func _integrate_forces(state):
|
||||||
|
if _in_vehicle:
|
||||||
|
linear_velocity = Vector3.ZERO
|
||||||
|
return
|
||||||
if cameraMoveMode and _pending_mouse_delta != Vector2.ZERO:
|
if cameraMoveMode and _pending_mouse_delta != Vector2.ZERO:
|
||||||
rotation_x -= _pending_mouse_delta.y * mouse_sensitivity
|
rotation_x -= _pending_mouse_delta.y * mouse_sensitivity
|
||||||
rotation_y -= _pending_mouse_delta.x * mouse_sensitivity
|
rotation_y -= _pending_mouse_delta.x * mouse_sensitivity
|
||||||
rotation_x = clamp(rotation_x, deg_to_rad(-90), deg_to_rad(90)) # Prevent flipping
|
rotation_x = clamp(rotation_x, deg_to_rad(-90), deg_to_rad(90)) # Prevent flipping
|
||||||
_camera_pitch = rotation_x
|
_camera_pitch = rotation_x
|
||||||
rotation.y = rotation_y
|
rotation.y = rotation_y
|
||||||
_pending_mouse_delta = Vector2.ZERO
|
_pending_mouse_delta = Vector2.ZERO
|
||||||
|
|
||||||
# Input as 2D vector
|
# Input as 2D vector
|
||||||
var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
|
var input2v := Input.get_vector("ui_left", "ui_right", "ui_up", "ui_down")
|
||||||
|
|
||||||
# Camera based movement
|
if Input.is_action_just_pressed("player_phone"):
|
||||||
var forward := Vector3.FORWARD * -1.0
|
phone_visible = !phone_visible
|
||||||
var right := Vector3.RIGHT
|
if phone:
|
||||||
if cam:
|
phone.visible = phone_visible
|
||||||
forward = cam.global_transform.basis.z
|
|
||||||
right = cam.global_transform.basis.x
|
# Camera based movement
|
||||||
# Project onto ground plane so looking up/down doesn't kill movement.
|
var forward := Vector3.FORWARD * -1.0
|
||||||
forward.y = 0.0
|
var right := Vector3.RIGHT
|
||||||
right.y = 0.0
|
if cam:
|
||||||
if forward.length() > 0.0001:
|
forward = cam.global_transform.basis.z
|
||||||
forward = forward.normalized()
|
right = cam.global_transform.basis.x
|
||||||
_last_move_forward = forward
|
# Project onto ground plane so looking up/down doesn't kill movement.
|
||||||
else:
|
forward.y = 0.0
|
||||||
forward = _last_move_forward
|
right.y = 0.0
|
||||||
if right.length() > 0.0001:
|
if forward.length() > 0.0001:
|
||||||
right = right.normalized()
|
forward = forward.normalized()
|
||||||
_last_move_right = right
|
_last_move_forward = forward
|
||||||
else:
|
else:
|
||||||
right = _last_move_right
|
forward = _last_move_forward
|
||||||
|
if right.length() > 0.0001:
|
||||||
var dir := (right * input2v.x + forward * input2v.y).normalized()
|
right = right.normalized()
|
||||||
var target_v := dir * MOVE_SPEED
|
_last_move_right = right
|
||||||
|
else:
|
||||||
var ax := ACCELLERATION if dir != Vector3.ZERO else DECELLERATION
|
right = _last_move_right
|
||||||
linear_velocity.x = move_toward(linear_velocity.x, target_v.x, ax * state.step)
|
|
||||||
linear_velocity.z = move_toward(linear_velocity.z, target_v.z, ax * state.step)
|
var dir := (right * input2v.x + forward * input2v.y).normalized()
|
||||||
|
var target_v := dir * MOVE_SPEED
|
||||||
# Jump Logic
|
|
||||||
var on_floor = false
|
var ax := ACCELLERATION if dir != Vector3.ZERO else DECELLERATION
|
||||||
for i in state.get_contact_count():
|
linear_velocity.x = move_toward(linear_velocity.x, target_v.x, ax * state.step)
|
||||||
var normal = state.get_contact_local_normal(i)
|
linear_velocity.z = move_toward(linear_velocity.z, target_v.z, ax * state.step)
|
||||||
if normal.y > 0.5:
|
|
||||||
on_floor = true
|
# Jump Logic
|
||||||
break
|
var on_floor = false
|
||||||
|
for i in state.get_contact_count():
|
||||||
|
var normal = state.get_contact_local_normal(i)
|
||||||
|
if normal.y > 0.5:
|
||||||
|
on_floor = true
|
||||||
|
break
|
||||||
|
|
||||||
if Input.is_action_just_pressed("ui_accept") and (on_floor or current_number_of_jumps == 1):
|
if Input.is_action_just_pressed("ui_accept") and (on_floor or current_number_of_jumps == 1):
|
||||||
current_number_of_jumps = (current_number_of_jumps + 1) % 2
|
current_number_of_jumps = (current_number_of_jumps + 1) % 2
|
||||||
linear_velocity.y = JUMP_SPEED
|
linear_velocity.y = JUMP_SPEED
|
||||||
audio_player.play()
|
audio_player.play()
|
||||||
|
_jump_triggered = true
|
||||||
|
|
||||||
if cam:
|
if cam:
|
||||||
var target_yaw := global_transform.basis.get_euler().y
|
var target_yaw := global_transform.basis.get_euler().y
|
||||||
@@ -118,29 +145,92 @@ func _integrate_forces(state):
|
|||||||
cam.global_position = cam.global_position.lerp(target_pos, camera_follow_speed * state.step)
|
cam.global_position = cam.global_position.lerp(target_pos, camera_follow_speed * state.step)
|
||||||
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0)
|
cam.global_rotation = Vector3(_camera_pitch, _camera_yaw, 0.0)
|
||||||
|
|
||||||
|
_update_animation(on_floor, state.linear_velocity)
|
||||||
|
_jump_triggered = false
|
||||||
|
|
||||||
func _input(event):
|
func _input(event):
|
||||||
|
if _in_vehicle:
|
||||||
|
return
|
||||||
if event is InputEventMouseButton:
|
if event is InputEventMouseButton:
|
||||||
if event.button_index == MOUSE_BUTTON_MIDDLE:
|
if event.button_index == MOUSE_BUTTON_MIDDLE:
|
||||||
if event.pressed:
|
if event.pressed:
|
||||||
cameraMoveMode = true
|
cameraMoveMode = true
|
||||||
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
|
||||||
else:
|
else:
|
||||||
cameraMoveMode = false
|
cameraMoveMode = false
|
||||||
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
|
||||||
|
|
||||||
if event is InputEventMouseMotion and cameraMoveMode:
|
if event is InputEventMouseMotion and cameraMoveMode:
|
||||||
_pending_mouse_delta += event.relative
|
_pending_mouse_delta += event.relative
|
||||||
|
|
||||||
if event is InputEventMouseButton and event.pressed:
|
if event is InputEventMouseButton and event.pressed:
|
||||||
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
if event.button_index == MOUSE_BUTTON_WHEEL_UP:
|
||||||
zoom_camera(1.0 / ZOOM_FACTOR) # Zoom in
|
zoom_camera(1.0 / ZOOM_FACTOR) # Zoom in
|
||||||
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
elif event.button_index == MOUSE_BUTTON_WHEEL_DOWN:
|
||||||
zoom_camera(ZOOM_FACTOR) # Zoom out
|
zoom_camera(ZOOM_FACTOR) # Zoom out
|
||||||
|
|
||||||
if event.is_action_pressed("player_light"):
|
if event.is_action_pressed("player_light"):
|
||||||
$SpotLight3D.visible = !$SpotLight3D.visible
|
_flashlight.visible = !_flashlight.visible
|
||||||
|
|
||||||
func zoom_camera(factor):
|
func zoom_camera(factor):
|
||||||
var new_fov = cam.fov * factor
|
var new_fov = cam.fov * factor
|
||||||
cam.fov = clamp(new_fov, MIN_FOV, MAX_FOV)
|
cam.fov = clamp(new_fov, MIN_FOV, MAX_FOV)
|
||||||
|
|
||||||
|
func _update_animation(on_floor: bool, velocity: Vector3) -> void:
|
||||||
|
if _anim_player == null:
|
||||||
|
return
|
||||||
|
var horizontal_speed := Vector3(velocity.x, 0.0, velocity.z).length()
|
||||||
|
if _jump_triggered and _anim_player.has_animation(anim_jump_name):
|
||||||
|
if _anim_player.current_animation != anim_jump_name:
|
||||||
|
_anim_player.play(anim_jump_name)
|
||||||
|
return
|
||||||
|
if not on_floor and _anim_player.has_animation(anim_jump_name):
|
||||||
|
if _anim_player.current_animation != anim_jump_name:
|
||||||
|
_anim_player.play(anim_jump_name)
|
||||||
|
return
|
||||||
|
if horizontal_speed > anim_walk_speed_threshold and _anim_player.has_animation(anim_walk_name):
|
||||||
|
if _anim_player.current_animation != anim_walk_name:
|
||||||
|
_anim_player.play(anim_walk_name)
|
||||||
|
return
|
||||||
|
if _anim_player.has_animation(anim_idle_name):
|
||||||
|
if _anim_player.current_animation != anim_idle_name:
|
||||||
|
_anim_player.play(anim_idle_name)
|
||||||
|
|
||||||
|
func enter_vehicle(_vehicle: Node, seat: Node3D, vehicle_camera: Camera3D) -> void:
|
||||||
|
_in_vehicle = true
|
||||||
|
freeze = true
|
||||||
|
sleeping = true
|
||||||
|
collision_layer = 0
|
||||||
|
collision_mask = 0
|
||||||
|
_vehicle_original_parent = get_parent()
|
||||||
|
_light_was_on = _flashlight.visible
|
||||||
|
_flashlight.visible = false
|
||||||
|
if _model_root:
|
||||||
|
_model_root.visible = false
|
||||||
|
if seat:
|
||||||
|
reparent(seat, true)
|
||||||
|
global_transform = seat.global_transform
|
||||||
|
if cam:
|
||||||
|
cam.current = false
|
||||||
|
if vehicle_camera:
|
||||||
|
vehicle_camera.current = true
|
||||||
|
|
||||||
|
func exit_vehicle(exit_point: Node3D, vehicle_camera: Camera3D) -> void:
|
||||||
|
_in_vehicle = false
|
||||||
|
freeze = false
|
||||||
|
sleeping = false
|
||||||
|
collision_layer = _vehicle_collision_layer
|
||||||
|
collision_mask = _vehicle_collision_mask
|
||||||
|
if _vehicle_original_parent:
|
||||||
|
reparent(_vehicle_original_parent, true)
|
||||||
|
_vehicle_original_parent = null
|
||||||
|
_flashlight.visible = _light_was_on
|
||||||
|
if _model_root:
|
||||||
|
_model_root.visible = true
|
||||||
|
if exit_point:
|
||||||
|
global_transform = exit_point.global_transform
|
||||||
|
if vehicle_camera:
|
||||||
|
vehicle_camera.current = false
|
||||||
|
if cam:
|
||||||
|
cam.current = true
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
uid://bpxggc8nr6tf6
|
uid://bpxggc8nr6tf6
|
||||||
|
|||||||
@@ -1,39 +1,39 @@
|
|||||||
[gd_resource type="Theme" load_steps=7 format=3 uid="uid://wpxmub0n2dr3"]
|
[gd_resource type="Theme" load_steps=7 format=3 uid="uid://wpxmub0n2dr3"]
|
||||||
|
|
||||||
[ext_resource type="FontFile" uid="uid://m5ceou0rk6j6" path="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" id="1_qnv5y"]
|
[ext_resource type="FontFile" uid="uid://m5ceou0rk6j6" path="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" id="1_qnv5y"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kwhvy"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_kwhvy"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bpa63"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_bpa63"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6abfs"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_6abfs"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hguu3"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_hguu3"]
|
||||||
|
|
||||||
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wu4fv"]
|
[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_wu4fv"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
Button/colors/font_color = Color(0.875, 0.875, 0.875, 1)
|
Button/colors/font_color = Color(0.875, 0.875, 0.875, 1)
|
||||||
Button/colors/font_disabled_color = Color(0.875, 0.875, 0.875, 0.5)
|
Button/colors/font_disabled_color = Color(0.875, 0.875, 0.875, 0.5)
|
||||||
Button/colors/font_focus_color = Color(0.95, 0.95, 0.95, 1)
|
Button/colors/font_focus_color = Color(0.95, 0.95, 0.95, 1)
|
||||||
Button/colors/font_hover_color = Color(0.95, 0.95, 0.95, 1)
|
Button/colors/font_hover_color = Color(0.95, 0.95, 0.95, 1)
|
||||||
Button/colors/font_hover_pressed_color = Color(1, 1, 1, 1)
|
Button/colors/font_hover_pressed_color = Color(1, 1, 1, 1)
|
||||||
Button/colors/font_outline_color = Color(8.834152, 8.190666, 9.316767, 1)
|
Button/colors/font_outline_color = Color(8.834152, 8.190666, 9.316767, 1)
|
||||||
Button/colors/font_pressed_color = Color(1, 1, 1, 1)
|
Button/colors/font_pressed_color = Color(1, 1, 1, 1)
|
||||||
Button/colors/icon_disabled_color = Color(1, 1, 1, 0.4)
|
Button/colors/icon_disabled_color = Color(1, 1, 1, 0.4)
|
||||||
Button/colors/icon_focus_color = Color(1, 1, 1, 1)
|
Button/colors/icon_focus_color = Color(1, 1, 1, 1)
|
||||||
Button/colors/icon_hover_color = Color(1, 1, 1, 1)
|
Button/colors/icon_hover_color = Color(1, 1, 1, 1)
|
||||||
Button/colors/icon_hover_pressed_color = Color(1, 1, 1, 1)
|
Button/colors/icon_hover_pressed_color = Color(1, 1, 1, 1)
|
||||||
Button/colors/icon_normal_color = Color(1, 1, 1, 1)
|
Button/colors/icon_normal_color = Color(1, 1, 1, 1)
|
||||||
Button/colors/icon_pressed_color = Color(1, 1, 1, 1)
|
Button/colors/icon_pressed_color = Color(1, 1, 1, 1)
|
||||||
Button/constants/align_to_largest_stylebox = 0
|
Button/constants/align_to_largest_stylebox = 0
|
||||||
Button/constants/h_separation = 4
|
Button/constants/h_separation = 4
|
||||||
Button/constants/icon_max_width = 0
|
Button/constants/icon_max_width = 0
|
||||||
Button/constants/outline_size = 0
|
Button/constants/outline_size = 0
|
||||||
Button/font_sizes/font_size = 32
|
Button/font_sizes/font_size = 32
|
||||||
Button/fonts/font = ExtResource("1_qnv5y")
|
Button/fonts/font = ExtResource("1_qnv5y")
|
||||||
Button/styles/disabled = SubResource("StyleBoxEmpty_kwhvy")
|
Button/styles/disabled = SubResource("StyleBoxEmpty_kwhvy")
|
||||||
Button/styles/focus = SubResource("StyleBoxEmpty_bpa63")
|
Button/styles/focus = SubResource("StyleBoxEmpty_bpa63")
|
||||||
Button/styles/hover = SubResource("StyleBoxEmpty_6abfs")
|
Button/styles/hover = SubResource("StyleBoxEmpty_6abfs")
|
||||||
Button/styles/normal = SubResource("StyleBoxEmpty_hguu3")
|
Button/styles/normal = SubResource("StyleBoxEmpty_hguu3")
|
||||||
Button/styles/pressed = SubResource("StyleBoxEmpty_wu4fv")
|
Button/styles/pressed = SubResource("StyleBoxEmpty_wu4fv")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://tn8qndst18d6"]
|
[gd_resource type="Theme" load_steps=2 format=3 uid="uid://tn8qndst18d6"]
|
||||||
|
|
||||||
[ext_resource type="FontFile" uid="uid://m5ceou0rk6j6" path="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" id="1_vd7w8"]
|
[ext_resource type="FontFile" uid="uid://m5ceou0rk6j6" path="res://assets/fonts/PlayfairDisplay-VariableFont_wght.ttf" id="1_vd7w8"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
Label/font_sizes/font_size = 60
|
Label/font_sizes/font_size = 60
|
||||||
Label/fonts/font = ExtResource("1_vd7w8")
|
Label/fonts/font = ExtResource("1_vd7w8")
|
||||||
|
|||||||
+428
-14
@@ -1,14 +1,428 @@
|
|||||||
# ---> VisualStudioCode
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
.vscode/*
|
## files generated by popular Visual Studio add-ons.
|
||||||
!.vscode/settings.json
|
##
|
||||||
!.vscode/tasks.json
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
!.vscode/launch.json
|
|
||||||
!.vscode/extensions.json
|
# User-specific files
|
||||||
!.vscode/*.code-snippets
|
*.rsuser
|
||||||
|
*.suo
|
||||||
# Local History for Visual Studio Code
|
*.user
|
||||||
.history/
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
# Built Visual Studio Code Extensions
|
*.env
|
||||||
*.vsix
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Mono auto generated files
|
||||||
|
mono_crash.*
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
|
||||||
|
[Dd]ebug/x64/
|
||||||
|
[Dd]ebugPublic/x64/
|
||||||
|
[Rr]elease/x64/
|
||||||
|
[Rr]eleases/x64/
|
||||||
|
bin/x64/
|
||||||
|
obj/x64/
|
||||||
|
|
||||||
|
[Dd]ebug/x86/
|
||||||
|
[Dd]ebugPublic/x86/
|
||||||
|
[Rr]elease/x86/
|
||||||
|
[Rr]eleases/x86/
|
||||||
|
bin/x86/
|
||||||
|
obj/x86/
|
||||||
|
|
||||||
|
[Ww][Ii][Nn]32/
|
||||||
|
[Aa][Rr][Mm]/
|
||||||
|
[Aa][Rr][Mm]64/
|
||||||
|
[Aa][Rr][Mm]64[Ee][Cc]/
|
||||||
|
bld/
|
||||||
|
[Oo]bj/
|
||||||
|
[Oo]ut/
|
||||||
|
[Ll]og/
|
||||||
|
[Ll]ogs/
|
||||||
|
|
||||||
|
# Build results on 'Bin' directories
|
||||||
|
**/[Bb]in/*
|
||||||
|
# Uncomment if you have tasks that rely on *.refresh files to move binaries
|
||||||
|
# (https://github.com/github/gitignore/pull/3736)
|
||||||
|
#!**/[Bb]in/*.refresh
|
||||||
|
|
||||||
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# Visual Studio 2017 auto generated files
|
||||||
|
Generated\ Files/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
*.trx
|
||||||
|
|
||||||
|
# NUnit
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
nunit-*.xml
|
||||||
|
|
||||||
|
# Approval Tests result files
|
||||||
|
*.received.*
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
|
||||||
|
# ASP.NET Scaffolding
|
||||||
|
ScaffoldingReadMe.txt
|
||||||
|
|
||||||
|
# StyleCop
|
||||||
|
StyleCopReport.xml
|
||||||
|
|
||||||
|
# Files built by Visual Studio
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_h.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.idb
|
||||||
|
*.iobj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.ipdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
# but not Directory.Build.rsp, as it configures directory-level build defaults
|
||||||
|
!Directory.Build.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*_wpftmp.csproj
|
||||||
|
*.log
|
||||||
|
*.tlog
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Coverlet is a free, cross platform Code Coverage Tool
|
||||||
|
coverage*.json
|
||||||
|
coverage*.xml
|
||||||
|
coverage*.info
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# NuGet Symbol Packages
|
||||||
|
*.snupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
*.appxbundle
|
||||||
|
*.appxupload
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!?*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Including strong name files can present a security risk
|
||||||
|
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||||
|
#*.snk
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
ServiceFabricBackup/
|
||||||
|
*.rptproj.bak
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
*.rptproj.rsuser
|
||||||
|
*- [Bb]ackup.rdl
|
||||||
|
*- [Bb]ackup ([0-9]).rdl
|
||||||
|
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace and project file (working project files containing files to include in project)
|
||||||
|
*.dsw
|
||||||
|
*.dsp
|
||||||
|
|
||||||
|
# Visual Studio 6 technical files
|
||||||
|
*.ncb
|
||||||
|
*.aps
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
**/.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
**/.fake/
|
||||||
|
|
||||||
|
# CodeRush personal settings
|
||||||
|
**/.cr/personal
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
**/__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
#tools/**
|
||||||
|
#!tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
|
||||||
|
# Azure Stream Analytics local run output
|
||||||
|
ASALocalRun/
|
||||||
|
|
||||||
|
# MSBuild Binary and Structured Log
|
||||||
|
*.binlog
|
||||||
|
MSBuild_Logs/
|
||||||
|
|
||||||
|
# AWS SAM Build and Temporary Artifacts folder
|
||||||
|
.aws-sam
|
||||||
|
|
||||||
|
# NVidia Nsight GPU debugger configuration file
|
||||||
|
*.nvuser
|
||||||
|
|
||||||
|
# MFractors (Xamarin productivity tool) working folder
|
||||||
|
**/.mfractor/
|
||||||
|
|
||||||
|
# Local History for Visual Studio
|
||||||
|
**/.localhistory/
|
||||||
|
|
||||||
|
# Visual Studio History (VSHistory) files
|
||||||
|
.vshistory/
|
||||||
|
|
||||||
|
# BeatPulse healthcheck temp database
|
||||||
|
healthchecksdb
|
||||||
|
|
||||||
|
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||||
|
MigrationBackup/
|
||||||
|
|
||||||
|
# Ionide (cross platform F# VS Code tools) working folder
|
||||||
|
**/.ionide/
|
||||||
|
|
||||||
|
# Fody - auto-generated XML schema
|
||||||
|
FodyWeavers.xsd
|
||||||
|
|
||||||
|
# VS Code files for those working on multiple tools
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
!.vscode/*.code-snippets
|
||||||
|
|
||||||
|
# Local History for Visual Studio Code
|
||||||
|
.history/
|
||||||
|
|
||||||
|
# Built Visual Studio Code Extensions
|
||||||
|
*.vsix
|
||||||
|
|
||||||
|
# Windows Installer files from build outputs
|
||||||
|
*.cab
|
||||||
|
*.msi
|
||||||
|
*.msix
|
||||||
|
*.msm
|
||||||
|
*.msp
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"Version": 1,
|
|
||||||
"WorkspaceRootPath": "Z:\\Godot\\Godot_v4.5-stable_mono_win64\\repos\\promiscuity\\microservices\\",
|
|
||||||
"Documents": [],
|
|
||||||
"DocumentGroupContainers": [
|
|
||||||
{
|
|
||||||
"Orientation": 0,
|
|
||||||
"VerticalTabListWidth": 256,
|
|
||||||
"DocumentGroups": [
|
|
||||||
{
|
|
||||||
"DockedWidth": 200,
|
|
||||||
"SelectedChildIndex": -1,
|
|
||||||
"Children": [
|
|
||||||
{
|
|
||||||
"$type": "Bookmark",
|
|
||||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"Version": 1,
|
|
||||||
"WorkspaceRootPath": "Z:\\Godot\\Godot_v4.5-stable_mono_win64\\repos\\promiscuity\\microservices\\",
|
|
||||||
"Documents": [],
|
|
||||||
"DocumentGroupContainers": [
|
|
||||||
{
|
|
||||||
"Orientation": 0,
|
|
||||||
"VerticalTabListWidth": 256,
|
|
||||||
"DocumentGroups": [
|
|
||||||
{
|
|
||||||
"DockedWidth": 200,
|
|
||||||
"SelectedChildIndex": -1,
|
|
||||||
"Children": [
|
|
||||||
{
|
|
||||||
"$type": "Bookmark",
|
|
||||||
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,17 +1,17 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
<Nullable>enable</Nullable>
|
<Nullable>enable</Nullable>
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Bcrypt.Net-Next" Version="4.0.3" />
|
<PackageReference Include="Bcrypt.Net-Next" Version="4.0.3" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
|
||||||
<PackageReference Include="MongoDB.Driver" Version="3.4.3" />
|
<PackageReference Include="MongoDB.Driver" Version="3.4.3" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ActiveDebugProfile>https</ActiveDebugProfile>
|
<ActiveDebugProfile>https</ActiveDebugProfile>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
</Project>
|
</Project>
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
@AuthApi_HostAddress = http://localhost:5279
|
@AuthApi_HostAddress = http://localhost:5279
|
||||||
|
|
||||||
GET {{AuthApi_HostAddress}}/weatherforecast/
|
GET {{AuthApi_HostAddress}}/weatherforecast/
|
||||||
Accept: application/json
|
Accept: application/json
|
||||||
|
|
||||||
###
|
###
|
||||||
|
|||||||
@@ -1,113 +1,113 @@
|
|||||||
using AuthApi.Models;
|
using AuthApi.Models;
|
||||||
using AuthApi.Services;
|
using AuthApi.Services;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using System.IdentityModel.Tokens.Jwt;
|
using System.IdentityModel.Tokens.Jwt;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace AuthApi.Controllers;
|
namespace AuthApi.Controllers;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Route("api/[controller]")]
|
[Route("api/[controller]")]
|
||||||
public class AuthController : ControllerBase
|
public class AuthController : ControllerBase
|
||||||
{
|
{
|
||||||
private readonly UserService _users;
|
private readonly UserService _users;
|
||||||
private readonly IConfiguration _cfg;
|
private readonly IConfiguration _cfg;
|
||||||
private readonly BlacklistService _blacklist;
|
private readonly BlacklistService _blacklist;
|
||||||
|
|
||||||
public AuthController(UserService users, IConfiguration cfg, BlacklistService blacklist)
|
public AuthController(UserService users, IConfiguration cfg, BlacklistService blacklist)
|
||||||
{
|
{
|
||||||
_users = users; _cfg = cfg; _blacklist = blacklist;
|
_users = users; _cfg = cfg; _blacklist = blacklist;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("register")]
|
[HttpPost("register")]
|
||||||
public async Task<IActionResult> Register([FromBody] RegisterRequest req)
|
public async Task<IActionResult> Register([FromBody] RegisterRequest req)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.Password))
|
if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.Password))
|
||||||
return BadRequest("Username and password required");
|
return BadRequest("Username and password required");
|
||||||
|
|
||||||
if (await _users.GetByUsernameAsync(req.Username) != null)
|
if (await _users.GetByUsernameAsync(req.Username) != null)
|
||||||
return BadRequest("User already exists");
|
return BadRequest("User already exists");
|
||||||
|
|
||||||
var hash = BCrypt.Net.BCrypt.HashPassword(req.Password);
|
var hash = BCrypt.Net.BCrypt.HashPassword(req.Password);
|
||||||
var user = new User { Username = req.Username, PasswordHash = hash, Role = "USER", Email = req.Email };
|
var user = new User { Username = req.Username, PasswordHash = hash, Role = "USER", Email = req.Email };
|
||||||
await _users.CreateAsync(user);
|
await _users.CreateAsync(user);
|
||||||
return Ok("User created");
|
return Ok("User created");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("login")]
|
[HttpPost("login")]
|
||||||
public async Task<IActionResult> Login([FromBody] LoginRequest req)
|
public async Task<IActionResult> Login([FromBody] LoginRequest req)
|
||||||
{
|
{
|
||||||
var user = await _users.GetByUsernameAsync(req.Username);
|
var user = await _users.GetByUsernameAsync(req.Username);
|
||||||
if (user == null || !BCrypt.Net.BCrypt.Verify(req.Password, user.PasswordHash))
|
if (user == null || !BCrypt.Net.BCrypt.Verify(req.Password, user.PasswordHash))
|
||||||
return Unauthorized();
|
return Unauthorized();
|
||||||
|
|
||||||
var (accessToken, jti, expUtc) = GenerateJwtToken(user);
|
var (accessToken, jti, expUtc) = GenerateJwtToken(user);
|
||||||
user.RefreshToken = Guid.NewGuid().ToString("N");
|
user.RefreshToken = Guid.NewGuid().ToString("N");
|
||||||
user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
|
user.RefreshTokenExpiry = DateTime.UtcNow.AddDays(7);
|
||||||
await _users.UpdateAsync(user);
|
await _users.UpdateAsync(user);
|
||||||
|
|
||||||
return Ok(new { accessToken, refreshToken = user.RefreshToken, user.Username, user.Role, jti, exp = expUtc });
|
return Ok(new { accessToken, refreshToken = user.RefreshToken, user.Username, user.Role, jti, exp = expUtc });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("refresh")]
|
[HttpPost("refresh")]
|
||||||
public async Task<IActionResult> Refresh([FromBody] RefreshRequest req)
|
public async Task<IActionResult> Refresh([FromBody] RefreshRequest req)
|
||||||
{
|
{
|
||||||
var user = await _users.GetByUsernameAsync(req.Username);
|
var user = await _users.GetByUsernameAsync(req.Username);
|
||||||
if (user == null || user.RefreshToken != req.RefreshToken || user.RefreshTokenExpiry < DateTime.UtcNow)
|
if (user == null || user.RefreshToken != req.RefreshToken || user.RefreshTokenExpiry < DateTime.UtcNow)
|
||||||
return Unauthorized("Invalid or expired refresh token");
|
return Unauthorized("Invalid or expired refresh token");
|
||||||
|
|
||||||
var (accessToken, _, expUtc) = GenerateJwtToken(user);
|
var (accessToken, _, expUtc) = GenerateJwtToken(user);
|
||||||
return Ok(new { accessToken, exp = expUtc });
|
return Ok(new { accessToken, exp = expUtc });
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("logout")]
|
[HttpPost("logout")]
|
||||||
[Authorize(Roles = "USER,SUPER")]
|
[Authorize(Roles = "USER,SUPER")]
|
||||||
public async Task<IActionResult> Logout()
|
public async Task<IActionResult> Logout()
|
||||||
{
|
{
|
||||||
var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", "");
|
var token = HttpContext.Request.Headers["Authorization"].FirstOrDefault()?.Replace("Bearer ", "");
|
||||||
if (string.IsNullOrWhiteSpace(token)) return BadRequest("Token missing");
|
if (string.IsNullOrWhiteSpace(token)) return BadRequest("Token missing");
|
||||||
var jwt = new JwtSecurityTokenHandler().ReadJwtToken(token);
|
var jwt = new JwtSecurityTokenHandler().ReadJwtToken(token);
|
||||||
await _blacklist.AddToBlacklistAsync(jwt.Id, jwt.ValidTo);
|
await _blacklist.AddToBlacklistAsync(jwt.Id, jwt.ValidTo);
|
||||||
return Ok("Logged out.");
|
return Ok("Logged out.");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("role")]
|
[HttpPost("role")]
|
||||||
[Authorize(Roles = "SUPER")]
|
[Authorize(Roles = "SUPER")]
|
||||||
public async Task<IActionResult> ChangeUserRole([FromBody] ChangeRoleRequest req)
|
public async Task<IActionResult> ChangeUserRole([FromBody] ChangeRoleRequest req)
|
||||||
{
|
{
|
||||||
if (req.NewRole is not ("USER" or "SUPER")) return BadRequest("Role must be 'USER' or 'SUPER'");
|
if (req.NewRole is not ("USER" or "SUPER")) return BadRequest("Role must be 'USER' or 'SUPER'");
|
||||||
var user = await _users.GetByUsernameAsync(req.Username);
|
var user = await _users.GetByUsernameAsync(req.Username);
|
||||||
if (user is null) return NotFound("User not found");
|
if (user is null) return NotFound("User not found");
|
||||||
user.Role = req.NewRole;
|
user.Role = req.NewRole;
|
||||||
await _users.UpdateAsync(user);
|
await _users.UpdateAsync(user);
|
||||||
return Ok($"{req.Username}'s role updated to {req.NewRole}");
|
return Ok($"{req.Username}'s role updated to {req.NewRole}");
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet("users")]
|
[HttpGet("users")]
|
||||||
[Authorize(Roles = "SUPER")]
|
[Authorize(Roles = "SUPER")]
|
||||||
public async Task<IActionResult> GetAllUsers() => Ok(await _users.GetAllAsync());
|
public async Task<IActionResult> GetAllUsers() => Ok(await _users.GetAllAsync());
|
||||||
|
|
||||||
private (string token, string jti, DateTime expUtc) GenerateJwtToken(User user)
|
private (string token, string jti, DateTime expUtc) GenerateJwtToken(User user)
|
||||||
{
|
{
|
||||||
var key = Encoding.UTF8.GetBytes(_cfg["Jwt:Key"]!);
|
var key = Encoding.UTF8.GetBytes(_cfg["Jwt:Key"]!);
|
||||||
var issuer = _cfg["Jwt:Issuer"] ?? "GameAuthApi";
|
var issuer = _cfg["Jwt:Issuer"] ?? "GameAuthApi";
|
||||||
var audience = _cfg["Jwt:Audience"] ?? issuer;
|
var audience = _cfg["Jwt:Audience"] ?? issuer;
|
||||||
|
|
||||||
var creds = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256);
|
var creds = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256);
|
||||||
var jti = Guid.NewGuid().ToString("N");
|
var jti = Guid.NewGuid().ToString("N");
|
||||||
var claims = new[]
|
var claims = new[]
|
||||||
{
|
{
|
||||||
new Claim(ClaimTypes.Name, user.Username),
|
new Claim(ClaimTypes.Name, user.Username),
|
||||||
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
new Claim(ClaimTypes.NameIdentifier, user.Id),
|
||||||
new Claim(ClaimTypes.Role, user.Role),
|
new Claim(ClaimTypes.Role, user.Role),
|
||||||
new Claim(JwtRegisteredClaimNames.Jti, jti)
|
new Claim(JwtRegisteredClaimNames.Jti, jti)
|
||||||
};
|
};
|
||||||
|
|
||||||
var exp = DateTime.UtcNow.AddMinutes(15);
|
var exp = DateTime.UtcNow.AddMinutes(15);
|
||||||
var token = new JwtSecurityToken(issuer, audience, claims, expires: exp, signingCredentials: creds);
|
var token = new JwtSecurityToken(issuer, audience, claims, expires: exp, signingCredentials: creds);
|
||||||
return (new JwtSecurityTokenHandler().WriteToken(token), jti, exp);
|
return (new JwtSecurityTokenHandler().WriteToken(token), jti, exp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# AuthApi document shapes
|
||||||
|
|
||||||
|
This service expects JSON request bodies for its auth endpoints and stores user
|
||||||
|
documents in MongoDB.
|
||||||
|
|
||||||
|
Inbound JSON documents
|
||||||
|
- RegisterRequest (`POST /api/auth/register`)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "string",
|
||||||
|
"password": "string",
|
||||||
|
"email": "string (optional)"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- LoginRequest (`POST /api/auth/login`)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "string",
|
||||||
|
"password": "string"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- RefreshRequest (`POST /api/auth/refresh`)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "string",
|
||||||
|
"refreshToken": "string"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
- ChangeRoleRequest (`POST /api/auth/role`)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"username": "string",
|
||||||
|
"newRole": "USER | SUPER"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Stored documents (MongoDB)
|
||||||
|
- User
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "string (ObjectId)",
|
||||||
|
"username": "string",
|
||||||
|
"passwordHash": "string",
|
||||||
|
"role": "USER | SUPER",
|
||||||
|
"email": "string (optional)",
|
||||||
|
"refreshToken": "string (optional)",
|
||||||
|
"refreshTokenExpiry": "string (optional, ISO-8601 datetime)"
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -1,21 +1,21 @@
|
|||||||
FROM mcr.microsoft.com/dotnet/sdk:9.0 AS build
|
FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build
|
||||||
WORKDIR /src
|
WORKDIR /src
|
||||||
|
|
||||||
# Copy project file first to take advantage of Docker layer caching
|
# Copy project file first to take advantage of Docker layer caching
|
||||||
COPY ["AuthApi.csproj", "./"]
|
COPY ["AuthApi.csproj", "./"]
|
||||||
RUN dotnet restore "AuthApi.csproj"
|
RUN dotnet restore "AuthApi.csproj"
|
||||||
|
|
||||||
# Copy the remaining source and publish
|
# Copy the remaining source and publish
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN dotnet publish "AuthApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
RUN dotnet publish "AuthApi.csproj" -c Release -o /app/publish /p:UseAppHost=false
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/aspnet:9.0 AS final
|
FROM mcr.microsoft.com/dotnet/aspnet:8.0 AS final
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
COPY --from=build /app/publish .
|
COPY --from=build /app/publish .
|
||||||
|
|
||||||
ENV ASPNETCORE_URLS=http://+:8080 \
|
ENV ASPNETCORE_URLS=http://+:8080 \
|
||||||
ASPNETCORE_ENVIRONMENT=Production
|
ASPNETCORE_ENVIRONMENT=Production
|
||||||
|
|
||||||
EXPOSE 8080
|
EXPOSE 8080
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "AuthApi.dll"]
|
ENTRYPOINT ["dotnet", "AuthApi.dll"]
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
namespace AuthApi.Models;
|
namespace AuthApi.Models;
|
||||||
|
|
||||||
public class RegisterRequest { public string Username { get; set; } = ""; public string Password { get; set; } = ""; public string? Email { get; set; } }
|
public class RegisterRequest { public string Username { get; set; } = ""; public string Password { get; set; } = ""; public string? Email { get; set; } }
|
||||||
public class LoginRequest { public string Username { get; set; } = ""; public string Password { get; set; } = ""; }
|
public class LoginRequest { public string Username { get; set; } = ""; public string Password { get; set; } = ""; }
|
||||||
public class ChangeRoleRequest { public string Username { get; set; } = ""; public string NewRole { get; set; } = ""; }
|
public class ChangeRoleRequest { public string Username { get; set; } = ""; public string NewRole { get; set; } = ""; }
|
||||||
public class RefreshRequest { public string Username { get; set; } = ""; public string RefreshToken { get; set; } = ""; }
|
public class RefreshRequest { public string Username { get; set; } = ""; public string RefreshToken { get; set; } = ""; }
|
||||||
|
|||||||
@@ -1,16 +1,16 @@
|
|||||||
using MongoDB.Bson;
|
using MongoDB.Bson;
|
||||||
using MongoDB.Bson.Serialization.Attributes;
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
|
|
||||||
namespace AuthApi.Models;
|
namespace AuthApi.Models;
|
||||||
|
|
||||||
public class User
|
public class User
|
||||||
{
|
{
|
||||||
[BsonId] [BsonRepresentation(BsonType.ObjectId)]
|
[BsonId] [BsonRepresentation(BsonType.ObjectId)]
|
||||||
public string Id { get; set; } = default!;
|
public string Id { get; set; } = default!;
|
||||||
public string Username { get; set; } = default!;
|
public string Username { get; set; } = default!;
|
||||||
public string PasswordHash { get; set; } = default!;
|
public string PasswordHash { get; set; } = default!;
|
||||||
public string Role { get; set; } = "USER";
|
public string Role { get; set; } = "USER";
|
||||||
public string? Email { get; set; }
|
public string? Email { get; set; }
|
||||||
public string? RefreshToken { get; set; }
|
public string? RefreshToken { get; set; }
|
||||||
public DateTime? RefreshTokenExpiry { get; set; }
|
public DateTime? RefreshTokenExpiry { get; set; }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,86 @@
|
|||||||
using AuthApi.Services;
|
using AuthApi.Services;
|
||||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||||
using Microsoft.IdentityModel.Tokens;
|
using Microsoft.IdentityModel.Tokens;
|
||||||
using Microsoft.OpenApi.Models;
|
using Microsoft.OpenApi.Models;
|
||||||
using System.Security.Claims;
|
using System.Security.Claims;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
builder.Services.AddControllers();
|
builder.Services.AddControllers();
|
||||||
|
|
||||||
// DI
|
// DI
|
||||||
builder.Services.AddSingleton<UserService>();
|
builder.Services.AddSingleton<UserService>();
|
||||||
builder.Services.AddSingleton<BlacklistService>();
|
builder.Services.AddSingleton<BlacklistService>();
|
||||||
|
|
||||||
// Swagger + JWT auth in Swagger
|
// Swagger + JWT auth in Swagger
|
||||||
builder.Services.AddEndpointsApiExplorer();
|
builder.Services.AddEndpointsApiExplorer();
|
||||||
builder.Services.AddSwaggerGen(c =>
|
builder.Services.AddSwaggerGen(c =>
|
||||||
{
|
{
|
||||||
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Auth API", Version = "v1" });
|
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Auth API", Version = "v1" });
|
||||||
c.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme
|
c.AddSecurityDefinition("bearerAuth", new OpenApiSecurityScheme
|
||||||
{
|
{
|
||||||
Type = SecuritySchemeType.Http,
|
Type = SecuritySchemeType.Http,
|
||||||
Scheme = "bearer",
|
Scheme = "bearer",
|
||||||
BearerFormat = "JWT",
|
BearerFormat = "JWT",
|
||||||
Description = "Paste your access token here (no 'Bearer ' prefix needed)."
|
Description = "Paste your access token here (no 'Bearer ' prefix needed)."
|
||||||
});
|
});
|
||||||
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
c.AddSecurityRequirement(new OpenApiSecurityRequirement
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
new OpenApiSecurityScheme
|
new OpenApiSecurityScheme
|
||||||
{
|
{
|
||||||
Reference = new OpenApiReference
|
Reference = new OpenApiReference
|
||||||
{ Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
|
{ Type = ReferenceType.SecurityScheme, Id = "bearerAuth" }
|
||||||
},
|
},
|
||||||
Array.Empty<string>()
|
Array.Empty<string>()
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// AuthN/JWT
|
// AuthN/JWT
|
||||||
var cfg = builder.Configuration;
|
var cfg = builder.Configuration;
|
||||||
var jwtKey = cfg["Jwt:Key"] ?? throw new Exception("Jwt:Key missing");
|
var jwtKey = cfg["Jwt:Key"] ?? throw new Exception("Jwt:Key missing");
|
||||||
var issuer = cfg["Jwt:Issuer"] ?? "GameAuthApi";
|
var issuer = cfg["Jwt:Issuer"] ?? "GameAuthApi";
|
||||||
var aud = cfg["Jwt:Audience"] ?? issuer;
|
var aud = cfg["Jwt:Audience"] ?? issuer;
|
||||||
|
|
||||||
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
|
||||||
.AddJwtBearer(o =>
|
.AddJwtBearer(o =>
|
||||||
{
|
{
|
||||||
o.TokenValidationParameters = new TokenValidationParameters
|
o.TokenValidationParameters = new TokenValidationParameters
|
||||||
{
|
{
|
||||||
ValidateIssuer = true, ValidIssuer = issuer,
|
ValidateIssuer = true, ValidIssuer = issuer,
|
||||||
ValidateAudience = true, ValidAudience = aud,
|
ValidateAudience = true, ValidAudience = aud,
|
||||||
ValidateIssuerSigningKey = true,
|
ValidateIssuerSigningKey = true,
|
||||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
|
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtKey)),
|
||||||
ValidateLifetime = true,
|
ValidateLifetime = true,
|
||||||
ClockSkew = TimeSpan.FromSeconds(30)
|
ClockSkew = TimeSpan.FromSeconds(30)
|
||||||
};
|
};
|
||||||
o.Events = new JwtBearerEvents
|
o.Events = new JwtBearerEvents
|
||||||
{
|
{
|
||||||
OnTokenValidated = async ctx =>
|
OnTokenValidated = async ctx =>
|
||||||
{
|
{
|
||||||
var jti = ctx.Principal?.FindFirstValue(System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Jti);
|
var jti = ctx.Principal?.FindFirstValue(System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames.Jti);
|
||||||
if (!string.IsNullOrEmpty(jti))
|
if (!string.IsNullOrEmpty(jti))
|
||||||
{
|
{
|
||||||
var bl = ctx.HttpContext.RequestServices.GetRequiredService<BlacklistService>();
|
var bl = ctx.HttpContext.RequestServices.GetRequiredService<BlacklistService>();
|
||||||
if (await bl.IsBlacklistedAsync(jti)) ctx.Fail("Token revoked");
|
if (await bl.IsBlacklistedAsync(jti)) ctx.Fail("Token revoked");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.Services.AddAuthorization();
|
builder.Services.AddAuthorization();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
app.MapGet("/healthz", () => Results.Ok("ok"));
|
app.MapGet("/healthz", () => Results.Ok("ok"));
|
||||||
app.UseSwagger();
|
app.UseSwagger();
|
||||||
app.UseSwaggerUI(o =>
|
app.UseSwaggerUI(o =>
|
||||||
{
|
{
|
||||||
o.SwaggerEndpoint("/swagger/v1/swagger.json", "Auth API v1");
|
o.SwaggerEndpoint("/swagger/v1/swagger.json", "Auth API v1");
|
||||||
o.RoutePrefix = "swagger";
|
o.RoutePrefix = "swagger";
|
||||||
});
|
});
|
||||||
app.UseAuthentication();
|
app.UseAuthentication();
|
||||||
app.UseAuthorization();
|
app.UseAuthorization();
|
||||||
app.MapControllers();
|
app.MapControllers();
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|||||||
@@ -1,23 +1,23 @@
|
|||||||
{
|
{
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
"$schema": "https://json.schemastore.org/launchsettings.json",
|
||||||
"profiles": {
|
"profiles": {
|
||||||
"http": {
|
"http": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": false,
|
||||||
"applicationUrl": "http://localhost:5279",
|
"applicationUrl": "http://localhost:5279",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"https": {
|
"https": {
|
||||||
"commandName": "Project",
|
"commandName": "Project",
|
||||||
"dotnetRunMessages": true,
|
"dotnetRunMessages": true,
|
||||||
"launchBrowser": false,
|
"launchBrowser": false,
|
||||||
"applicationUrl": "https://localhost:7295;http://localhost:5279",
|
"applicationUrl": "https://localhost:7295;http://localhost:5279",
|
||||||
"environmentVariables": {
|
"environmentVariables": {
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
# AuthApi
|
||||||
|
|
||||||
|
## Document shapes
|
||||||
|
See `DOCUMENTS.md` for request payloads and stored document shapes.
|
||||||
|
|
||||||
|
## Endpoints
|
||||||
|
- `POST /api/auth/register` Register a new user.
|
||||||
|
- `POST /api/auth/login` Issue access and refresh tokens.
|
||||||
|
- `POST /api/auth/refresh` Refresh an access token.
|
||||||
|
- `POST /api/auth/logout` Revoke the current access token.
|
||||||
|
- `POST /api/auth/role` Update a user's role (SUPER only).
|
||||||
|
- `GET /api/auth/users` List users (SUPER only).
|
||||||
@@ -1,36 +1,36 @@
|
|||||||
using MongoDB.Bson.Serialization.Attributes;
|
using MongoDB.Bson.Serialization.Attributes;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
|
||||||
namespace AuthApi.Services;
|
namespace AuthApi.Services;
|
||||||
|
|
||||||
public class BlacklistedToken
|
public class BlacklistedToken
|
||||||
{
|
{
|
||||||
[BsonId] public string Jti { get; set; } = default!;
|
[BsonId] public string Jti { get; set; } = default!;
|
||||||
public DateTime ExpiresAt { get; set; }
|
public DateTime ExpiresAt { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class BlacklistService
|
public class BlacklistService
|
||||||
{
|
{
|
||||||
private readonly IMongoCollection<BlacklistedToken> _col;
|
private readonly IMongoCollection<BlacklistedToken> _col;
|
||||||
|
|
||||||
public BlacklistService(IConfiguration cfg)
|
public BlacklistService(IConfiguration cfg)
|
||||||
{
|
{
|
||||||
var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
|
var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
|
||||||
var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb";
|
var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb";
|
||||||
var client = new MongoClient(cs);
|
var client = new MongoClient(cs);
|
||||||
var db = client.GetDatabase(dbName);
|
var db = client.GetDatabase(dbName);
|
||||||
_col = db.GetCollection<BlacklistedToken>("BlacklistedTokens");
|
_col = db.GetCollection<BlacklistedToken>("BlacklistedTokens");
|
||||||
|
|
||||||
// TTL index so revocations expire automatically
|
// TTL index so revocations expire automatically
|
||||||
var keys = Builders<BlacklistedToken>.IndexKeys.Ascending(x => x.ExpiresAt);
|
var keys = Builders<BlacklistedToken>.IndexKeys.Ascending(x => x.ExpiresAt);
|
||||||
_col.Indexes.CreateOne(new CreateIndexModel<BlacklistedToken>(keys, new CreateIndexOptions { ExpireAfter = TimeSpan.Zero }));
|
_col.Indexes.CreateOne(new CreateIndexModel<BlacklistedToken>(keys, new CreateIndexOptions { ExpireAfter = TimeSpan.Zero }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task AddToBlacklistAsync(string jti, DateTime expiresAt) =>
|
public Task AddToBlacklistAsync(string jti, DateTime expiresAt) =>
|
||||||
_col.ReplaceOneAsync(x => x.Jti == jti,
|
_col.ReplaceOneAsync(x => x.Jti == jti,
|
||||||
new BlacklistedToken { Jti = jti, ExpiresAt = expiresAt },
|
new BlacklistedToken { Jti = jti, ExpiresAt = expiresAt },
|
||||||
new ReplaceOptions { IsUpsert = true });
|
new ReplaceOptions { IsUpsert = true });
|
||||||
|
|
||||||
public Task<bool> IsBlacklistedAsync(string jti) =>
|
public Task<bool> IsBlacklistedAsync(string jti) =>
|
||||||
_col.Find(x => x.Jti == jti).AnyAsync();
|
_col.Find(x => x.Jti == jti).AnyAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,32 +1,32 @@
|
|||||||
using AuthApi.Models;
|
using AuthApi.Models;
|
||||||
using MongoDB.Driver;
|
using MongoDB.Driver;
|
||||||
|
|
||||||
namespace AuthApi.Services;
|
namespace AuthApi.Services;
|
||||||
|
|
||||||
public class UserService
|
public class UserService
|
||||||
{
|
{
|
||||||
private readonly IMongoCollection<User> _col;
|
private readonly IMongoCollection<User> _col;
|
||||||
|
|
||||||
public UserService(IConfiguration cfg)
|
public UserService(IConfiguration cfg)
|
||||||
{
|
{
|
||||||
var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
|
var cs = cfg["MongoDB:ConnectionString"] ?? "mongodb://127.0.0.1:27017";
|
||||||
var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb";
|
var dbName = cfg["MongoDB:DatabaseName"] ?? "GameDb";
|
||||||
var client = new MongoClient(cs);
|
var client = new MongoClient(cs);
|
||||||
var db = client.GetDatabase(dbName);
|
var db = client.GetDatabase(dbName);
|
||||||
_col = db.GetCollection<User>("Users");
|
_col = db.GetCollection<User>("Users");
|
||||||
|
|
||||||
var keys = Builders<User>.IndexKeys.Ascending(u => u.Username);
|
var keys = Builders<User>.IndexKeys.Ascending(u => u.Username);
|
||||||
_col.Indexes.CreateOne(new CreateIndexModel<User>(keys, new CreateIndexOptions { Unique = true }));
|
_col.Indexes.CreateOne(new CreateIndexModel<User>(keys, new CreateIndexOptions { Unique = true }));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<User?> GetByUsernameAsync(string username) =>
|
public Task<User?> GetByUsernameAsync(string username) =>
|
||||||
_col.Find(u => u.Username == username).FirstOrDefaultAsync();
|
_col.Find(u => u.Username == username).FirstOrDefaultAsync();
|
||||||
|
|
||||||
public Task CreateAsync(User user) => _col.InsertOneAsync(user);
|
public Task CreateAsync(User user) => _col.InsertOneAsync(user);
|
||||||
|
|
||||||
public Task UpdateAsync(User user) =>
|
public Task UpdateAsync(User user) =>
|
||||||
_col.ReplaceOneAsync(u => u.Id == user.Id, user);
|
_col.ReplaceOneAsync(u => u.Id == user.Id, user);
|
||||||
|
|
||||||
public Task<List<User>> GetAllAsync() =>
|
public Task<List<User>> GetAllAsync() =>
|
||||||
_col.Find(FilterDefinition<User>.Empty).ToListAsync();
|
_col.Find(FilterDefinition<User>.Empty).ToListAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Information",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Kestrel": { "Endpoints": { "Http": { "Url": "http://0.0.0.0:5000" } } },
|
"Kestrel": { "Endpoints": { "Http": { "Url": "http://0.0.0.0:5000" } } },
|
||||||
"MongoDB": { "ConnectionString": "mongodb://192.168.86.50:27017", "DatabaseName": "promiscuity" },
|
"MongoDB": { "ConnectionString": "mongodb://192.168.86.50:27017", "DatabaseName": "promiscuity" },
|
||||||
"Jwt": { "Key": "SuperUltraSecureJwtKeyWithAtLeast32Chars!!", "Issuer": "promiscuity", "Audience": "promiscuity-auth-api" },
|
"Jwt": { "Key": "SuperUltraSecureJwtKeyWithAtLeast32Chars!!", "Issuer": "promiscuity", "Audience": "promiscuity-auth-api" },
|
||||||
"Logging": { "LogLevel": { "Default": "Information" } },
|
"Logging": { "LogLevel": { "Default": "Information" } },
|
||||||
"AllowedHosts": "*"
|
"AllowedHosts": "*"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,476 +0,0 @@
|
|||||||
{
|
|
||||||
"runtimeTarget": {
|
|
||||||
"name": ".NETCoreApp,Version=v8.0",
|
|
||||||
"signature": ""
|
|
||||||
},
|
|
||||||
"compilationOptions": {},
|
|
||||||
"targets": {
|
|
||||||
".NETCoreApp,Version=v8.0": {
|
|
||||||
"AuthApi/1.0.0": {
|
|
||||||
"dependencies": {
|
|
||||||
"BCrypt.Net-Next": "4.0.3",
|
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer": "8.0.8",
|
|
||||||
"Microsoft.AspNetCore.OpenApi": "8.0.8",
|
|
||||||
"MongoDB.Driver": "3.4.3",
|
|
||||||
"Swashbuckle.AspNetCore": "6.5.0"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"AuthApi.dll": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"BCrypt.Net-Next/4.0.3": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net6.0/BCrypt.Net-Next.dll": {
|
|
||||||
"assemblyVersion": "4.0.3.0",
|
|
||||||
"fileVersion": "4.0.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"DnsClient/1.6.1": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Win32.Registry": "5.0.0"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net5.0/DnsClient.dll": {
|
|
||||||
"assemblyVersion": "1.6.1.0",
|
|
||||||
"fileVersion": "1.6.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer/8.0.8": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {
|
|
||||||
"assemblyVersion": "8.0.8.0",
|
|
||||||
"fileVersion": "8.0.824.36908"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.AspNetCore.OpenApi/8.0.8": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.OpenApi": "1.4.3"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.AspNetCore.OpenApi.dll": {
|
|
||||||
"assemblyVersion": "8.0.8.0",
|
|
||||||
"fileVersion": "8.0.824.36908"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.ApiDescription.Server/6.0.5": {},
|
|
||||||
"Microsoft.Extensions.Logging.Abstractions/2.0.0": {},
|
|
||||||
"Microsoft.IdentityModel.Abstractions/7.1.2": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.IdentityModel.Abstractions.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.JsonWebTokens/7.1.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.Tokens": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.IdentityModel.JsonWebTokens.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Logging/7.1.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.Abstractions": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.IdentityModel.Logging.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Protocols/7.1.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.Logging": "7.1.2",
|
|
||||||
"Microsoft.IdentityModel.Tokens": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.IdentityModel.Protocols.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/7.1.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.Protocols": "7.1.2",
|
|
||||||
"System.IdentityModel.Tokens.Jwt": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Tokens/7.1.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.Logging": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/Microsoft.IdentityModel.Tokens.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Platforms/5.0.0": {},
|
|
||||||
"Microsoft.OpenApi/1.4.3": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/netstandard2.0/Microsoft.OpenApi.dll": {
|
|
||||||
"assemblyVersion": "1.4.3.0",
|
|
||||||
"fileVersion": "1.4.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Microsoft.Win32.Registry/5.0.0": {
|
|
||||||
"dependencies": {
|
|
||||||
"System.Security.AccessControl": "5.0.0",
|
|
||||||
"System.Security.Principal.Windows": "5.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MongoDB.Bson/3.4.3": {
|
|
||||||
"dependencies": {
|
|
||||||
"System.Memory": "4.5.5",
|
|
||||||
"System.Runtime.CompilerServices.Unsafe": "5.0.0"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net6.0/MongoDB.Bson.dll": {
|
|
||||||
"assemblyVersion": "3.4.3.0",
|
|
||||||
"fileVersion": "3.4.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"MongoDB.Driver/3.4.3": {
|
|
||||||
"dependencies": {
|
|
||||||
"DnsClient": "1.6.1",
|
|
||||||
"Microsoft.Extensions.Logging.Abstractions": "2.0.0",
|
|
||||||
"MongoDB.Bson": "3.4.3",
|
|
||||||
"SharpCompress": "0.30.1",
|
|
||||||
"Snappier": "1.0.0",
|
|
||||||
"System.Buffers": "4.5.1",
|
|
||||||
"ZstdSharp.Port": "0.7.3"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net6.0/MongoDB.Driver.dll": {
|
|
||||||
"assemblyVersion": "3.4.3.0",
|
|
||||||
"fileVersion": "3.4.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"SharpCompress/0.30.1": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net5.0/SharpCompress.dll": {
|
|
||||||
"assemblyVersion": "0.30.1.0",
|
|
||||||
"fileVersion": "0.30.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Snappier/1.0.0": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net5.0/Snappier.dll": {
|
|
||||||
"assemblyVersion": "1.0.0.0",
|
|
||||||
"fileVersion": "1.0.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore/6.5.0": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.Extensions.ApiDescription.Server": "6.0.5",
|
|
||||||
"Swashbuckle.AspNetCore.Swagger": "6.5.0",
|
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen": "6.5.0",
|
|
||||||
"Swashbuckle.AspNetCore.SwaggerUI": "6.5.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore.Swagger/6.5.0": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.OpenApi": "1.4.3"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net7.0/Swashbuckle.AspNetCore.Swagger.dll": {
|
|
||||||
"assemblyVersion": "6.5.0.0",
|
|
||||||
"fileVersion": "6.5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen/6.5.0": {
|
|
||||||
"dependencies": {
|
|
||||||
"Swashbuckle.AspNetCore.Swagger": "6.5.0"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {
|
|
||||||
"assemblyVersion": "6.5.0.0",
|
|
||||||
"fileVersion": "6.5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore.SwaggerUI/6.5.0": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net7.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {
|
|
||||||
"assemblyVersion": "6.5.0.0",
|
|
||||||
"fileVersion": "6.5.0.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Buffers/4.5.1": {},
|
|
||||||
"System.IdentityModel.Tokens.Jwt/7.1.2": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.IdentityModel.JsonWebTokens": "7.1.2",
|
|
||||||
"Microsoft.IdentityModel.Tokens": "7.1.2"
|
|
||||||
},
|
|
||||||
"runtime": {
|
|
||||||
"lib/net8.0/System.IdentityModel.Tokens.Jwt.dll": {
|
|
||||||
"assemblyVersion": "7.1.2.0",
|
|
||||||
"fileVersion": "7.1.2.41121"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Memory/4.5.5": {},
|
|
||||||
"System.Runtime.CompilerServices.Unsafe/5.0.0": {},
|
|
||||||
"System.Security.AccessControl/5.0.0": {
|
|
||||||
"dependencies": {
|
|
||||||
"Microsoft.NETCore.Platforms": "5.0.0",
|
|
||||||
"System.Security.Principal.Windows": "5.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"System.Security.Principal.Windows/5.0.0": {},
|
|
||||||
"ZstdSharp.Port/0.7.3": {
|
|
||||||
"runtime": {
|
|
||||||
"lib/net7.0/ZstdSharp.dll": {
|
|
||||||
"assemblyVersion": "0.7.3.0",
|
|
||||||
"fileVersion": "0.7.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"libraries": {
|
|
||||||
"AuthApi/1.0.0": {
|
|
||||||
"type": "project",
|
|
||||||
"serviceable": false,
|
|
||||||
"sha512": ""
|
|
||||||
},
|
|
||||||
"BCrypt.Net-Next/4.0.3": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-W+U9WvmZQgi5cX6FS5GDtDoPzUCV4LkBLkywq/kRZhuDwcbavOzcDAr3LXJFqHUi952Yj3LEYoWW0jbEUQChsA==",
|
|
||||||
"path": "bcrypt.net-next/4.0.3",
|
|
||||||
"hashPath": "bcrypt.net-next.4.0.3.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"DnsClient/1.6.1": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-4H/f2uYJOZ+YObZjpY9ABrKZI+JNw3uizp6oMzTXwDw6F+2qIPhpRl/1t68O/6e98+vqNiYGu+lswmwdYUy3gg==",
|
|
||||||
"path": "dnsclient/1.6.1",
|
|
||||||
"hashPath": "dnsclient.1.6.1.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.AspNetCore.Authentication.JwtBearer/8.0.8": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-J145j2LgD4kkkNkrf5DW/pKzithZRKN5EFY+KAO3SqweMyDfv4cgKgtOIsv2bhrOLGqPJixuZkZte7LfK1seYQ==",
|
|
||||||
"path": "microsoft.aspnetcore.authentication.jwtbearer/8.0.8",
|
|
||||||
"hashPath": "microsoft.aspnetcore.authentication.jwtbearer.8.0.8.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.AspNetCore.OpenApi/8.0.8": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-wNHhohqP8rmsQ4UhKbd6jZMD6l+2Q/+DvRBT0Cgqeuglr13aF6sSJWicZKCIhZAUXzuhkdwtHVc95MlPlFk0dA==",
|
|
||||||
"path": "microsoft.aspnetcore.openapi/8.0.8",
|
|
||||||
"hashPath": "microsoft.aspnetcore.openapi.8.0.8.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.ApiDescription.Server/6.0.5": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-Ckb5EDBUNJdFWyajfXzUIMRkhf52fHZOQuuZg/oiu8y7zDCVwD0iHhew6MnThjHmevanpxL3f5ci2TtHQEN6bw==",
|
|
||||||
"path": "microsoft.extensions.apidescription.server/6.0.5",
|
|
||||||
"hashPath": "microsoft.extensions.apidescription.server.6.0.5.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.Extensions.Logging.Abstractions/2.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==",
|
|
||||||
"path": "microsoft.extensions.logging.abstractions/2.0.0",
|
|
||||||
"hashPath": "microsoft.extensions.logging.abstractions.2.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Abstractions/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-33eTIA2uO/L9utJjZWbKsMSVsQf7F8vtd6q5mQX7ZJzNvCpci5fleD6AeANGlbbb7WX7XKxq9+Dkb5e3GNDrmQ==",
|
|
||||||
"path": "microsoft.identitymodel.abstractions/7.1.2",
|
|
||||||
"hashPath": "microsoft.identitymodel.abstractions.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.JsonWebTokens/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-cloLGeZolXbCJhJBc5OC05uhrdhdPL6MWHuVUnkkUvPDeK7HkwThBaLZ1XjBQVk9YhxXE2OvHXnKi0PLleXxDg==",
|
|
||||||
"path": "microsoft.identitymodel.jsonwebtokens/7.1.2",
|
|
||||||
"hashPath": "microsoft.identitymodel.jsonwebtokens.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Logging/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-YCxBt2EeJP8fcXk9desChkWI+0vFqFLvBwrz5hBMsoh0KJE6BC66DnzkdzkJNqMltLromc52dkdT206jJ38cTw==",
|
|
||||||
"path": "microsoft.identitymodel.logging/7.1.2",
|
|
||||||
"hashPath": "microsoft.identitymodel.logging.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Protocols/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-SydLwMRFx6EHPWJ+N6+MVaoArN1Htt92b935O3RUWPY1yUF63zEjvd3lBu79eWdZUwedP8TN2I5V9T3nackvIQ==",
|
|
||||||
"path": "microsoft.identitymodel.protocols/7.1.2",
|
|
||||||
"hashPath": "microsoft.identitymodel.protocols.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Protocols.OpenIdConnect/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-6lHQoLXhnMQ42mGrfDkzbIOR3rzKM1W1tgTeMPLgLCqwwGw0d96xFi/UiX/fYsu7d6cD5MJiL3+4HuI8VU+sVQ==",
|
|
||||||
"path": "microsoft.identitymodel.protocols.openidconnect/7.1.2",
|
|
||||||
"hashPath": "microsoft.identitymodel.protocols.openidconnect.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.IdentityModel.Tokens/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-oICJMqr3aNEDZOwnH5SK49bR6Z4aX0zEAnOLuhloumOSuqnNq+GWBdQyrgILnlcT5xj09xKCP/7Y7gJYB+ls/g==",
|
|
||||||
"path": "microsoft.identitymodel.tokens/7.1.2",
|
|
||||||
"hashPath": "microsoft.identitymodel.tokens.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.NETCore.Platforms/5.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-VyPlqzH2wavqquTcYpkIIAQ6WdenuKoFN0BdYBbCWsclXacSOHNQn66Gt4z5NBqEYW0FAPm5rlvki9ZiCij5xQ==",
|
|
||||||
"path": "microsoft.netcore.platforms/5.0.0",
|
|
||||||
"hashPath": "microsoft.netcore.platforms.5.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.OpenApi/1.4.3": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-rURwggB+QZYcSVbDr7HSdhw/FELvMlriW10OeOzjPT7pstefMo7IThhtNtDudxbXhW+lj0NfX72Ka5EDsG8x6w==",
|
|
||||||
"path": "microsoft.openapi/1.4.3",
|
|
||||||
"hashPath": "microsoft.openapi.1.4.3.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Microsoft.Win32.Registry/5.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-dDoKi0PnDz31yAyETfRntsLArTlVAVzUzCIvvEDsDsucrl33Dl8pIJG06ePTJTI3tGpeyHS9Cq7Foc/s4EeKcg==",
|
|
||||||
"path": "microsoft.win32.registry/5.0.0",
|
|
||||||
"hashPath": "microsoft.win32.registry.5.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"MongoDB.Bson/3.4.3": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-ZB2nCdlWtmDGItkDFh2E2kfYlXaItG414t9Np7CZhpftLypemYnxtdI52H+0b8RPqoUJD7bUvrf598sDTJd5iA==",
|
|
||||||
"path": "mongodb.bson/3.4.3",
|
|
||||||
"hashPath": "mongodb.bson.3.4.3.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"MongoDB.Driver/3.4.3": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-yE6XQiDoFwTH4Xq/STJCbzsz+74RuzCXU45g9gaWFlLyy95xG8utuj+e64uXSbONtzabbp1O/8vfA3/HJXL6Pg==",
|
|
||||||
"path": "mongodb.driver/3.4.3",
|
|
||||||
"hashPath": "mongodb.driver.3.4.3.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"SharpCompress/0.30.1": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-XqD4TpfyYGa7QTPzaGlMVbcecKnXy4YmYLDWrU+JIj7IuRNl7DH2END+Ll7ekWIY8o3dAMWLFDE1xdhfIWD1nw==",
|
|
||||||
"path": "sharpcompress/0.30.1",
|
|
||||||
"hashPath": "sharpcompress.0.30.1.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Snappier/1.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-rFtK2KEI9hIe8gtx3a0YDXdHOpedIf9wYCEYtBEmtlyiWVX3XlCNV03JrmmAi/Cdfn7dxK+k0sjjcLv4fpHnqA==",
|
|
||||||
"path": "snappier/1.0.0",
|
|
||||||
"hashPath": "snappier.1.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore/6.5.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-FK05XokgjgwlCI6wCT+D4/abtQkL1X1/B9Oas6uIwHFmYrIO9WUD5aLC9IzMs9GnHfUXOtXZ2S43gN1mhs5+aA==",
|
|
||||||
"path": "swashbuckle.aspnetcore/6.5.0",
|
|
||||||
"hashPath": "swashbuckle.aspnetcore.6.5.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore.Swagger/6.5.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-XWmCmqyFmoItXKFsQSwQbEAsjDKcxlNf1l+/Ki42hcb6LjKL8m5Db69OTvz5vLonMSRntYO1XLqz0OP+n3vKnA==",
|
|
||||||
"path": "swashbuckle.aspnetcore.swagger/6.5.0",
|
|
||||||
"hashPath": "swashbuckle.aspnetcore.swagger.6.5.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore.SwaggerGen/6.5.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-Y/qW8Qdg9OEs7V013tt+94OdPxbRdbhcEbw4NiwGvf4YBcfhL/y7qp/Mjv/cENsQ2L3NqJ2AOu94weBy/h4KvA==",
|
|
||||||
"path": "swashbuckle.aspnetcore.swaggergen/6.5.0",
|
|
||||||
"hashPath": "swashbuckle.aspnetcore.swaggergen.6.5.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"Swashbuckle.AspNetCore.SwaggerUI/6.5.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-OvbvxX+wL8skxTBttcBsVxdh73Fag4xwqEU2edh4JMn7Ws/xJHnY/JB1e9RoCb6XpDxUF3hD9A0Z1lEUx40Pfw==",
|
|
||||||
"path": "swashbuckle.aspnetcore.swaggerui/6.5.0",
|
|
||||||
"hashPath": "swashbuckle.aspnetcore.swaggerui.6.5.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.Buffers/4.5.1": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-Rw7ijyl1qqRS0YQD/WycNst8hUUMgrMH4FCn1nNm27M4VxchZ1js3fVjQaANHO5f3sN4isvP4a+Met9Y4YomAg==",
|
|
||||||
"path": "system.buffers/4.5.1",
|
|
||||||
"hashPath": "system.buffers.4.5.1.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.IdentityModel.Tokens.Jwt/7.1.2": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-Thhbe1peAmtSBFaV/ohtykXiZSOkx59Da44hvtWfIMFofDA3M3LaVyjstACf2rKGn4dEDR2cUpRAZ0Xs/zB+7Q==",
|
|
||||||
"path": "system.identitymodel.tokens.jwt/7.1.2",
|
|
||||||
"hashPath": "system.identitymodel.tokens.jwt.7.1.2.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.Memory/4.5.5": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-XIWiDvKPXaTveaB7HVganDlOCRoj03l+jrwNvcge/t8vhGYKvqV+dMv6G4SAX2NoNmN0wZfVPTAlFwZcZvVOUw==",
|
|
||||||
"path": "system.memory/4.5.5",
|
|
||||||
"hashPath": "system.memory.4.5.5.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.Runtime.CompilerServices.Unsafe/5.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-ZD9TMpsmYJLrxbbmdvhwt9YEgG5WntEnZ/d1eH8JBX9LBp+Ju8BSBhUGbZMNVHHomWo2KVImJhTDl2hIgw/6MA==",
|
|
||||||
"path": "system.runtime.compilerservices.unsafe/5.0.0",
|
|
||||||
"hashPath": "system.runtime.compilerservices.unsafe.5.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.Security.AccessControl/5.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-dagJ1mHZO3Ani8GH0PHpPEe/oYO+rVdbQjvjJkBRNQkX4t0r1iaeGn8+/ybkSLEan3/slM0t59SVdHzuHf2jmw==",
|
|
||||||
"path": "system.security.accesscontrol/5.0.0",
|
|
||||||
"hashPath": "system.security.accesscontrol.5.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"System.Security.Principal.Windows/5.0.0": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-t0MGLukB5WAVU9bO3MGzvlGnyJPgUlcwerXn1kzBRjwLKixT96XV0Uza41W49gVd8zEMFu9vQEFlv0IOrytICA==",
|
|
||||||
"path": "system.security.principal.windows/5.0.0",
|
|
||||||
"hashPath": "system.security.principal.windows.5.0.0.nupkg.sha512"
|
|
||||||
},
|
|
||||||
"ZstdSharp.Port/0.7.3": {
|
|
||||||
"type": "package",
|
|
||||||
"serviceable": true,
|
|
||||||
"sha512": "sha512-U9Ix4l4cl58Kzz1rJzj5hoVTjmbx1qGMwzAcbv1j/d3NzrFaESIurQyg+ow4mivCgkE3S413y+U9k4WdnEIkRA==",
|
|
||||||
"path": "zstdsharp.port/0.7.3",
|
|
||||||
"hashPath": "zstdsharp.port.0.7.3.nupkg.sha512"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"runtimeOptions": {
|
|
||||||
"tfm": "net8.0",
|
|
||||||
"frameworks": [
|
|
||||||
{
|
|
||||||
"name": "Microsoft.NETCore.App",
|
|
||||||
"version": "8.0.0"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "Microsoft.AspNetCore.App",
|
|
||||||
"version": "8.0.0"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"configProperties": {
|
|
||||||
"System.GC.Server": true,
|
|
||||||
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user