Convert MistoxWebsite.Client submodule to normal folder

This commit is contained in:
2025-06-16 19:04:55 -07:00
parent dc7a7b65ec
commit 5a5da3e62e
35 changed files with 10157 additions and 1 deletions
Submodule src/MistoxWebsite.Client deleted from db1a58746c
+92
View File
@@ -0,0 +1,92 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "projects",
"projects": {
"Mistox-Frontend": {
"projectType": "application",
"schematics": {},
"root": "",
"sourceRoot": "src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular/build:application",
"options": {
"browser": "src/main.ts",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kB",
"maximumError": "1MB"
},
{
"type": "anyComponentStyle",
"maximumWarning": "4kB",
"maximumError": "8kB"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular/build:dev-server",
"configurations": {
"production": {
"buildTarget": "Mistox-Frontend:build:production"
},
"development": {
"buildTarget": "Mistox-Frontend:build:development"
}
},
"defaultConfiguration": "development"
},
"extract-i18n": {
"builder": "@angular/build:extract-i18n"
},
"test": {
"builder": "@angular/build:karma",
"options": {
"polyfills": [
"zone.js",
"zone.js/testing"
],
"tsConfig": "tsconfig.spec.json",
"assets": [
{
"glob": "**/*",
"input": "public"
}
],
"styles": [
"src/styles.css"
]
}
}
}
}
}
}
File diff suppressed because it is too large Load Diff
+36
View File
@@ -0,0 +1,36 @@
{
"name": "mistox-frontend",
"version": "0.0.0",
"scripts": {
"ng": "ng",
"start": "ng serve",
"build": "ng build",
"watch": "ng build --watch --configuration development",
"test": "ng test"
},
"private": true,
"dependencies": {
"@angular/common": "^20.0.0",
"@angular/compiler": "^20.0.0",
"@angular/core": "^20.0.0",
"@angular/forms": "^20.0.0",
"@angular/platform-browser": "^20.0.0",
"@angular/router": "^20.0.0",
"rxjs": "~7.8.0",
"tslib": "^2.3.0",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular/build": "^20.0.2",
"@angular/cli": "^20.0.2",
"@angular/compiler-cli": "^20.0.0",
"@types/jasmine": "~5.1.0",
"jasmine-core": "~5.7.0",
"karma": "~6.4.0",
"karma-chrome-launcher": "~3.2.0",
"karma-coverage": "~2.2.0",
"karma-jasmine": "~5.1.0",
"karma-jasmine-html-reporter": "~2.1.0",
"typescript": "~5.8.2"
}
}
@@ -0,0 +1,13 @@
import { ApplicationConfig, provideBrowserGlobalErrorListeners, provideZoneChangeDetection } from '@angular/core';
import { provideRouter } from '@angular/router';
import { routes } from './app.routes';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
export const appConfig: ApplicationConfig = {
providers: [
provideBrowserGlobalErrorListeners(),
provideZoneChangeDetection({ eventCoalescing: true }),
provideRouter(routes),
provideHttpClient(withInterceptorsFromDi())
]
};
+200
View File
@@ -0,0 +1,200 @@
.navbar-toggler {
background-color: var(--Mistox-Dark);
color: var(--Mistox-White);
width: 100%;
height: 40px;
border: none;
font-size: 20px;
transition-duration: 0.5s;
}
.navbar-toggler:hover {
background-color: #410a04;
}
.top-row {
background-color: var(--Mistox-Offset);
height: 200px;
}
.bottom-row {
height: calc(100% - 200px);
background: linear-gradient(0deg, var(--Mistox-Dark), var(--Mistox-Offset) );
}
.navbar-brand img {
width: 200px;
height: 200px;
padding: 0 25px;
}
.oi {
width: 2rem;
font-size: 1.1rem;
color: var(--Mistox-White);
}
.nav-item {
font-size: 0.9rem;
padding-bottom: 0.5rem;
text-decoration: none;
}
.nav-item:first-of-type {
padding-top: 1rem;
}
.nav-item:last-of-type {
padding-bottom: 1rem;
}
.nav-item a {
color: var(--Mistox-White);
border-radius: 4px;
height: 3rem;
display: flex;
align-items: center;
line-height: 3rem;
transition-duration: 0.5s;
text-decoration: none;
padding-left: 20px;
}
.nav-item a.active {
background-color: rgba(255,255,255,0.25);
color: var(--Mistox-White);
}
.nav-item a:hover {
background-color: rgba(255,255,255,0.1);
color: var(--Mistox-White);
}
.nav-login {
position: relative;
bottom: 10px;
left: 10px;
width: calc(100% - 20px);
padding-top: 10px;
}
.collapse {
display: none;
}
.nav-login-button {
display: inline-block;
width: calc(50% - 2.5px);
background-color: transparent;
border-radius: 5px;
border-color: transparent;
color: var(--Mistox-White);
transition-duration: 0.5s;
padding: 5px 0;
padding-top: 9px;
text-align: center;
text-decoration: none;
font-size: 15;
}
.nav-login-button:hover {
background-color: #FFFFFF50;
}
article{
padding: 0 !important;
}
.page {
position: relative;
display: flex;
flex-direction: column;
background-color: var(--Mistox-Black);
}
body{
background-color: var(--Mistox-Black);
}
main {
flex: 1;
background-color: var(--Mistox-Black);
color: var(--Mistox-White);
}
.sidebar {
border-right: var(--Mistox-Background) 2px solid;
}
@media (max-width: 640.98px) {
.top-row:not(.auth) {
display: none;
}
.top-row.auth {
justify-content: space-between;
}
.top-row ::deep a, .top-row ::deep .btn-link {
margin-left: 0;
}
}
@media (min-width: 641px) {
.page {
flex-direction: row;
}
.sidebar {
width: 250px;
height: 100vh;
position: sticky;
top: 0;
min-width: 250px;
}
.top-row {
position: sticky;
top: 0;
z-index: 1;
}
.top-row.auth ::deep a:first-child {
flex: 1;
text-align: right;
width: 0;
}
.navbar-toggler {
display: none;
}
.collapse {
/* Never collapse the sidebar for wide screens */
display: block !important;
}
.nav-login {
position: absolute;
}
}
@media (max-width: 640px){
.navbar-brand img {
position: relative !important;
padding-left: calc(50% - 80px) !important;
height: 160px;
width: 160px;
}
.top-gradient {
background: linear-gradient(0deg, var(--Mistox-Dark), var(--Mistox-Offset) );
}
.bottom-row {
background: var(--Mistox-Medium);
}
}
+63
View File
@@ -0,0 +1,63 @@
<div class="page">
<!-- Sidebar Start -->
<div class="sidebar">
<div class="top-row">
<div class="top-gradient">
<a class="navbar-brand" href="">
<img src="images/logo.png" />
</a>
</div>
<button title="DropDownButtonMobile" class="navbar-toggler">
<span class="navbar-toggler-icon">MENU</span>
</button>
</div>
<div class="bottom-row">
<div class="@NavMenuCssClass">
<nav class="flex-column">
<!-- Home -->
<div class="nav-item">
<a #homeLink class="nav-link" href="">
<span>Home</span>
</a>
</div>
<!-- Project Mist -->
<div class="nav-item">
<a #mistLink class="nav-link" href="/project/mist">
<span>Project-Mist</span>
</a>
</div>
<!-- Store -->
<div class="nav-item">
<a #storeLink class="nav-link" href="/store/catalog">
<span>Store</span>
</a>
</div>
<!-- About -->
<div class="nav-item">
<a #aboutLink class="nav-link" href="/about">
<span>About</span>
</a>
</div>
<!-- Login Stuff -->
<div class="nav-login">
<div *ngIf="auth.isLoggedIn">
<a class="nav-login-button" href="/account/settings"><span>{{ auth.loggedInUser.userName }}</span></a>
<a class="nav-login-button" href="/account/logout"><span>Logout</span></a>
</div>
<div *ngIf="!auth.isLoggedIn">
<a class="nav-login-button" href="/account/login"><span>Login</span></a>
<a class="nav-login-button" href="/account/register"><span>Register</span></a>
</div>
</div>
</nav>
</div>
</div>
</div>
<!-- Sidebar End -->
<main>
<article class="content px-4">
<router-outlet />
</article>
</main>
</div>
@@ -0,0 +1,28 @@
import { Routes } from '@angular/router';
import { ForgotPasswordComponent } from './pages/account/forgotpassword/forgotpassword.component';
import { LoginComponent } from './pages/account/login/login.component';
import { RegisterComponent } from './pages/account/register/register.component';
import { MistComponent } from './pages/project/mist/mist.component';
import { CatalogComponent } from './pages/store/catalog/catalog.component';
import { AboutComponent } from './pages/legal/about/about.component';
import { SettingsComponent } from './pages/account/settings/settings.component';
import { LogoutComponent } from './pages/account/logout/logout.component';
export const routes: Routes = [
// Account stuff
{ path: "account/forgotpassword", component: ForgotPasswordComponent },
{ path: "account/login", component: LoginComponent },
{ path: "account/logout", component: LogoutComponent },
{ path: "account/register", component: RegisterComponent },
{ path: "account/settings", component: SettingsComponent },
// Projects
{ path: "project/mist", component: MistComponent },
// Store
{ path: "store/catalog", component: CatalogComponent },
// Legal
{ path: "about", component: AboutComponent },
]
+30
View File
@@ -0,0 +1,30 @@
import { Component, ElementRef, ViewChild } from '@angular/core';
import { Router, RouterOutlet } from '@angular/router';
import { Authentication } from './services/Authentication';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-root',
imports: [RouterOutlet, CommonModule],
templateUrl: './app.html',
styleUrl: './app.css'
})
export class App {
@ViewChild('homeLink') homeLink!: ElementRef<HTMLAnchorElement>;
@ViewChild('mistLink') mistLink!: ElementRef<HTMLAnchorElement>;
@ViewChild('storeLink') storeLink!: ElementRef<HTMLAnchorElement>;
@ViewChild('aboutLink') aboutLink!: ElementRef<HTMLAnchorElement>;
constructor(public auth: Authentication, private router: Router){}
ngAfterViewInit(){
let ViewLinks = [ this.homeLink, this.mistLink, this.storeLink, this.aboutLink ];
ViewLinks.forEach(link => {
if (new URL(link.nativeElement.href).pathname === new URL(window.location.href).pathname){
link.nativeElement.classList.add("active");
}
});
}
}
@@ -0,0 +1,15 @@
import { WebSiteData } from "./WebsiteData";
export class Account {
public id: number = -1;
public userName: string = "";
public email: string = "";
public emailVerified: boolean = false;
public passwordHash: string = "";
public siteData: WebSiteData = new WebSiteData();
public error: string = "";
constructor(init?: Partial<Account>) {
Object.assign(this, init);
}
}
@@ -0,0 +1,12 @@
export class WebSiteData {
public accountID: number = -1;
public failedPasswordLock: boolean = false;
public passwordAttempts: number = 5;
public currentPasswordAttempts: number = 0;
public role: string = "Generic";
public emailToken: string = "";
constructor(init?: Partial<WebSiteData>) {
Object.assign(this, init);
}
}
@@ -0,0 +1,19 @@
<form class="center big-frame background-border" #accountForm="ngForm" (ngSubmit)="onSubmit()">
<div class="frame-item">
<input type="text" [(ngModel)]="user.email" name="email" placeholder=" " />
<label>Email</label>
</div>
<div>
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="Send Code" />
</div>
</div>
</div>
<ul *ngFor="let msg of errorMsgs" >
<li>{{ msg }}</li>
</ul>
</form>
@@ -0,0 +1,43 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
@Component({
selector: 'account-forgot',
templateUrl: './forgotpassword.component.html',
imports: [ FormsModule, CommonModule ]
})
export class ForgotPasswordComponent {
user!: Account;
errorMsgs: string[] = [];
returnURL: string = '/';
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
this.title.setTitle("Forgot Password | Mistox");
this.route.queryParams.subscribe(params => {
this.returnURL = params['returnURL'] || '/';
});
}
onSubmit() {
this.errorMsgs = [];
this.errorMsgs.push("Waiting for login response from server");
this.http.post<Account>( "api/account/sendresetpassword", this.user, { responseType: 'json' } ).subscribe({
next: data => {
if (data.error.length === 0){
this.router.navigate([this.returnURL]);
}else{
this.errorMsgs = [];
this.errorMsgs.push(data.error);
}
},
error: err => {
this.errorMsgs.push("HTTP Error: ", err);
}
});
}
}
@@ -0,0 +1,33 @@
<form class="center big-frame background-border" #accountForm="ngForm" (ngSubmit)="onSubmit()">
<h3>Login</h3>
<div class="frame-item">
<input type="text" [(ngModel)]="user.userName" name="userName" placeholder=" " autocomplete="username" />
<label>UserName</label>
</div>
<div class="frame-item">
<input type="password" [(ngModel)]="user.passwordHash" name="password" placeholder=" " autocomplete="current-password" />
<label>Password</label>
</div>
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="submit" value="LOGIN" />
</div>
<div class="frame-forgot">
<div class="sub-frame">
Stay Logged In
<input type="checkbox" [(ngModel)]="user.emailVerified" name="stayLoggedIn" />
</div>
<div class="sub-frame">
<a routerLink="/account/forgotpassword">Forgot Password</a>
</div>
</div>
</div>
<ul *ngFor="let msg of errorMsgs" >
<li>{{ msg }}</li>
</ul>
</form>
@@ -0,0 +1,61 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { Authentication, SessionType } from '../../../services/Authentication';
@Component({
selector: 'account-login',
templateUrl: './login.component.html',
imports: [ FormsModule, CommonModule ],
standalone: true
})
export class LoginComponent {
user: Account = { id: -1, userName: "", email: "", passwordHash: "", emailVerified: false, error: "", siteData: { accountID: -1, failedPasswordLock: false, passwordAttempts: 0, role: "", currentPasswordAttempts: 0, emailToken: "" } };
errorMsgs: string[] = [];
returnURL: string = '/';
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Login | Mistox");
this.route.queryParams.subscribe(params => {
this.returnURL = params['returnURL'] || '/';
});
}
onSubmit() {
this.errorMsgs = [];
if (!this.user.userName) {
this.errorMsgs.push("The 'username' field is required");
}
if (!this.user.passwordHash) {
this.errorMsgs.push("The 'password' field is required");
}
if (this.user.passwordHash.length < 6) {
this.errorMsgs.push("Password must be at least 6 Characters long");
}
if (this.errorMsgs.length > 0) {
return;
}
let sType: SessionType = SessionType.Session;
if( this.user.emailVerified ){
sType = SessionType.Forever;
}
this.errorMsgs.push("Waiting for login response from server");
this.auth.Login(this.user, sType).subscribe(
data => {
if (data.error.length === 0){
this.router.navigate([this.returnURL]);
}else{
this.errorMsgs.pop();
this.errorMsgs.push(data.error);
}
}
)
}
}
@@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { Authentication } from '../../../services/Authentication';
@Component({
selector: 'account-login',
templateUrl: './logout.component.html',
imports: [ FormsModule, CommonModule ],
standalone: true
})
export class LogoutComponent {
user: Account = { id: -1, userName: "", email: "", passwordHash: "", emailVerified: false, error: "", siteData: { accountID: -1, failedPasswordLock: false, passwordAttempts: 0, role: "", currentPasswordAttempts: 0, emailToken: "" } };
errorMsgs: string[] = [];
returnURL: string = '/';
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Logout | Mistox");
}
ngAfterViewInit(){
this.auth.Logout();
this.router.navigate(["/"]);
}
}
@@ -0,0 +1,33 @@
<form class="center big-frame background-border" #accountForm="ngForm" (ngSubmit)="onSubmit()">
<h3>Register</h3>
<div class="frame-item">
<input type="text" [(ngModel)]="user.userName" name="userName" placeholder=" " autocomplete="username" />
<label>UserName</label>
</div>
<div class="frame-item">
<input type="email" [(ngModel)]="user.email" name="password" placeholder=" " autocomplete="current-password" />
<label>Email</label>
</div>
<div class="frame-item">
<input type="password" [(ngModel)]="user.passwordHash" name="password" placeholder=" " autocomplete="current-password" />
<label>Password</label>
</div>
<div class="frame-item">
<input type="password" [(ngModel)]="user.error" name="password" placeholder=" " autocomplete="current-password" />
<label>Password</label>
</div>
<div class="flex-row">
<div class="frame-button">
<input class="submit" type="button" value="LOGIN" />
</div>
</div>
<ul *ngFor="let msg of errorMsgs" >
<li>{{ msg }}</li>
</ul>
</form>
@@ -0,0 +1,61 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
@Component({
selector: 'account-register',
templateUrl: './register.component.html',
imports: [ FormsModule, CommonModule ]
})
export class RegisterComponent {
user!: Account;
errorMsgs: string[] = [];
returnURL: string = '/';
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
this.title.setTitle("Register | Mistox");
this.route.queryParams.subscribe(params => {
this.returnURL = params['returnURL'] || '/';
});
}
onSubmit() {
this.errorMsgs = [];
if (!this.user.userName) {
this.errorMsgs.push("The 'username' field is required");
}
if (!this.user.passwordHash) {
this.errorMsgs.push("The 'password' field is required");
}
if (this.user.passwordHash.length < 6) {
this.errorMsgs.push("Password must be at least 6 Characters long");
}
if (this.user.passwordHash !== this.user.error){
this.errorMsgs.push("Passwords don't match");
}
if (this.errorMsgs.length > 0) {
return;
}
this.user.error = "";
this.errorMsgs.push("Waiting for login response from server");
this.http.post<Account>( "api/account/register", this.user, { responseType: 'json' } ).subscribe({
next: data => {
if (data.error.length === 0){
this.router.navigate([this.returnURL]);
}else{
this.errorMsgs = [];
this.errorMsgs.push(data.error);
}
},
error: err => {
this.errorMsgs.push("HTTP Error: ", err);
}
});
}
}
@@ -0,0 +1,29 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Account } from '../../../models/Account';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
@Component({
selector: 'account-register',
templateUrl: './settings.component.html',
imports: [ FormsModule, CommonModule ]
})
export class SettingsComponent {
user!: Account;
errorMsgs: string[] = [];
returnURL: string = '/';
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
this.title.setTitle("Settings | Mistox");
this.route.queryParams.subscribe(params => {
this.returnURL = params['returnURL'] || '/';
});
}
onSubmit() {
}
}
@@ -0,0 +1,34 @@
<style>
.about-frame {
margin-top: 25px;
max-width: 1200px;
padding: 10px;
}
</style>
<div class="big-frame background-border horizontal-center about-frame">
<p>Welcome to Mistox LLC. A project and hobby of Derek Holloway.</p>
<br />
<p>I am an indi-developer who has been making small projects since I was 13. I originally learned lua and spent 4 years mastering it. Then I moved onto C# which is my preferred language</p>
<p>My programming catalog consist of C#, Lua, SQL, C++, C, and JavaScript in the order of knowledge from best to passiable.</p>
<p>Im currently in college for computer sciences and should honestly be doing that instead of this but I find working on this website and hobby games to be way more enjoyable.</p>
<br />
<p>I would love to learn how to use Blender in order to make all the models for my games but with the amount of work ive already made for myself im going to hold off for now.</p>
<p>This website and everything on it are the long countless hours of my time and motivation to create something that I can be proud of and share that with the world.</p>
<p>So if you would like to support me as a small creator please feel free to leave a donation from on the store page. It would means a lot to me.</p>
<br />
<p>For the nerds out there, this website is a blazor webassembly app, hosted on an ubuntu webserver, with a mysql backend.</p>
<p>All the passwords are encrypted using bcrypt for your safety and all the data is only allowed through SSL.</p>
<p>After you make your account. All the data in the database is easily accessable through the account settings and</p>
<p>you can delete your account at any time. Including all your data with it so there is no risk.</p>
<p>I wont show ads and never will and I refuse to use trackers on this site.</p>
<br />
<br />
<p>If you have any questions, concerns, or would like to suggest a feature, bug-fix, or request to help. Please feel</p>
<p>free to reach out to me at <a href="mailto://derek@mistox.net">derek&commat;mistox.net</a></p>
<a href='https://ko-fi.com/A0A3TSI2D' target='_blank'>
<img height='36' style='border:0px;height:36px;' src='https://storage.ko-fi.com/cdn/kofi6.png?v=6' alt='Buy Me a Coffee at ko-fi.com' />
</a>
</div>
@@ -0,0 +1,19 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
@Component({
selector: 'account-forgot',
templateUrl: './about.component.html',
imports: [ FormsModule, CommonModule ]
})
export class AboutComponent {
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
this.title.setTitle("About | Mistox");
};
}
@@ -0,0 +1,38 @@
<style>
.mist-frame {
margin-top: 25px;
max-width: 1000px;
padding: 10px;
}
.Big-Div {
position: relative;
left: calc(50% - 400px);
max-width: 800px;
}
@media (max-width: 1150px) {
.Big-Div {
position: relative;
left: calc(50% - 200px);
max-width: 400px;
}
}
</style>
<div class="big-frame background-border horizontal-center mist-frame">
<p><strong>What is the game</strong></p>
<p style="padding-left: 30px;">Project-Mist is a survival game. Kind of like a battle royal in a sense but, think of it backwards. And no I know what your thinking. Its not the first person to die wins. No instead its a never ending survival game where you can free roam and build structures. The catch is, the person who has the highest stats [i.e A combination of kills, survival time] has a marker placed on their forhead.</p>
<p><strong>How will the game play</strong></p>
<p style="padding-left: 30px;">When you join the game you will be able to customize your character. There you can set a default loadout for your player. This will be the spawn weapon and gear. After that you will drop into the map with other players to fend for your life. The kill-leader will be marked loosely on the mini-map. You can choose to go after the kill leader or you can choose to loot first. The choice is yours. But be aware that if you survive long enough you will become the new kill leader.</p>
<p><strong>Current Idea Board *SUBJECT TO CHANGE*</strong></p>
<p>Survival Game<br />look at item to pick up 'e' for third person and click for third [No nearby]<br />normal weapons with bullet drop bullet travel time<br />snipers but rare [Maybe special]</p>
<p>Abilities selectable at spawn<br /> a max 20 credit slider where you can spend them on traits<br /> Stamina -&gt; run for longer distances<br /> Strength -&gt; carry more weight<br /> Vitality -&gt; Have more base health<br /> Stealth -&gt; Approximate location on map is bigger</p>
<p>More weight slows player some<br />Backpacks -&gt; Add slots but not weight</p>
<p>Oddball style game<br /> Map that shows the relitive area of the top player</p>
<p>spawn with classes<br /> 2 mags<br /> no attachments<br /> unlock guns with experience</p>
<p>no health regen<br />final hit headshots = 20 credits<br />final hit bodyshots = 10 credits</p>
<p>classes require credits to spawn with better stuff<br />inventory and credits are transferrable between servers and sessions<br />combat loggging - if leave in combat start from scratch<br />one dynamicly roaming entity of the night ( Impossible to kill, when near heart starts pumping and vinegrette )<br /> goes after people possible to get away<br />Dyanmic day and night cycle<br />Dynamic weather ( rain, fog, thunder, lightning )<br />floods that cause roaring rivers to fill that cannot be swam<br />Fires that char trees(no leaves), regrows in 3ish days<br />Master leaderboard in the main menu of top players per rank<br />Ranked lobby ( Disabled until player base )<br />small towns around a main centralized area( ie city or temple )<br />large servers<br />random spawned skin boxes that require credits to open<br />purchasable skins<br />bullet penatration on certain materials<br />bullet reflection on certain materials<br />No kill leader until you get at least 2 kills minimum</p>
<p>Tournament mode<br /> all players spawn at the same time<br /> hold oddball for 30mins total</p>
<p>server quits introducing people into game after 5 hrs. (last man standing mode)<br /> last person in server wins<br /> on death quit to new server<br /> everyone becomes oddball<br /> less players alive equal less oddball area</p>
</div>
@@ -0,0 +1,19 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
@Component({
selector: 'account-forgot',
templateUrl: './mist.component.html',
imports: [ FormsModule, CommonModule ]
})
export class MistComponent {
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title ) {
this.title.setTitle("Mist | Mistox");
};
}
@@ -0,0 +1 @@
<span>Temp Store</span>
@@ -0,0 +1,20 @@
import { Component } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { Router, ActivatedRoute } from '@angular/router';
import { Title } from '@angular/platform-browser';
import { CommonModule } from '@angular/common';
import { Authentication } from '../../../services/Authentication';
@Component({
selector: 'account-forgot',
templateUrl: './catalog.component.html',
imports: [ FormsModule, CommonModule ]
})
export class CatalogComponent {
constructor( private http: HttpClient, private router: Router, private route: ActivatedRoute, private title: Title, public auth: Authentication ) {
this.title.setTitle("Store | Mistox");
};
}
@@ -0,0 +1,75 @@
import { Injectable } from "@angular/core";
import { Account } from "../models/Account";
import { BehaviorSubject, Observable } from "rxjs";
import { HttpClient } from "@angular/common/http";
@Injectable({ providedIn: 'root' })
export class Authentication{
private _user = new BehaviorSubject<Account>(this.getUserFromStorage());
user$ = this._user.asObservable();
constructor( private http: HttpClient){ }
Login(LoginCredentials: Account, StayLoggedIn :SessionType): Observable<Account> {
let sub = this.http.post<Account>( "https://mistox.com/api/account/login", LoginCredentials, { responseType: 'json' } );
sub.subscribe({
next: data => {
if (data.error.length === 0){
this._user.next(data);
this.setUserToStorage(data, StayLoggedIn);
}
},
error: err => {
console.log("HTTP Error Signing In: ", err);
}
});
return sub;
}
Logout(){
this.http.post<Account>( "https://mistox.com/api/account/logout", {}, { responseType: 'json' } ).subscribe( );
this._user.next( new Account );
this.delUserFromStorage();
}
get isLoggedIn(): boolean {
return this._user.value.id != -1 ? true : false;
}
get loggedInUser(): Account {
return this._user.value;
}
private getUserFromStorage(): Account {
const foreverUser = localStorage.getItem('user');
const sessionUser = sessionStorage.getItem('user');
let user = null;
if (foreverUser != null){
user = JSON.parse(foreverUser)
} else if (sessionUser != null){
user = JSON.parse(sessionUser)
} else {
user = new Account();
user.id = -1;
}
return user;
}
private setUserToStorage(user: Account, session: SessionType): void {
if (session == SessionType.Forever){
localStorage.setItem('user', JSON.stringify(user));
}else if(session == SessionType.Session){
sessionStorage.setItem('user', JSON.stringify(user));
}
}
private delUserFromStorage(): void {
localStorage.removeItem('user');
sessionStorage.removeItem('user');
}
}
export enum SessionType {
Forever,
Session
}
+13
View File
@@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Mistox</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="favicon.ico">
</head>
<body style="border: 0; padding: 0; margin: 0;">
<app-root></app-root>
</body>
</html>
+5
View File
@@ -0,0 +1,5 @@
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import { App } from './app/app';
bootstrapApplication(App, appConfig).catch((err) => console.error(err));
+152
View File
@@ -0,0 +1,152 @@
/* You can add global styles to this file, and also import other style files */
:root {
--Mistox-Dark: #2C0703;
--Mistox-Medium: #890620;
--Mistox-Light: #B6465F;
--Mistox-Bright: #FC440F;
--Mistox-Offset: #443B75;
--Mistox-Background: #320000;
--Mistox-White: #FFF;
--Mistox-Black: #000;
}
html, body {
font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
}
main {
background-color: #000000;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='28' height='49' viewBox='0 0 28 49'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='hexagons' fill='%23ff0000' fill-opacity='0.2' fill-rule='nonzero'%3E%3Cpath d='M13.99 9.25l13 7.5v15l-13 7.5L1 31.75v-15l12.99-7.5zM3 17.9v12.7l10.99 6.34 11-6.35V17.9l-11-6.34L3 17.9zM0 15l12.98-7.5V0h-2v6.35L0 12.69v2.3zm0 18.5L12.98 41v8h-2v-6.85L0 35.81v-2.3zM15 0v7.5L27.99 15H28v-2.31h-.01L17 6.35V0h-2zm0 49v-8l12.99-7.5H28v2.31h-.01L17 42.15V49h-2z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
}
/* CSS used for the Account Activity Pages */
.center {
position: relative;
left: 50%;
top: 50vh;
transform: translateY(-50%) translateX(-50%);
}
.horizontal-center {
position: relative;
left: 50%;
transform: translateX(-50%);
}
.vertical-center {
position: relative;
top: 50vh;
transform: translateY(-50%);
}
.background-border {
border: var(--Mistox-Background) 2px solid;
border-radius: 6px;
}
.big-frame {
background-color: var(--Mistox-Black);
padding: 4px;
max-width: 400px;
color: var(--Mistox-White);
transition-duration: 1s;
}
.big-frame h3{
margin: 15px 0 30px 0;
color: var(--Mistox-White);
text-align: center;
}
.big-frame h2{
text-align: center;
position: relative;
margin: 0;
top: -20px;
font-size: 15px;
color: var(--Mistox-Bright);
}
.big-frame .frame-item label{
position: relative;
padding: 10px 0;
font-size: 16px;
color: var(--Mistox-White);
pointer-events: none;
transition: .5s;
top: -70px;
left: 20px;
}
.big-frame .frame-item input:autofill,
.big-frame .frame-item input:-webkit-autofill,
.big-frame .frame-item input {
position: relative;
width: calc(100% - 40px);
margin: 0 20px;
padding: 10px 0;
font-size: 15px;
color: var(--Mistox-White);
margin-bottom: 30px;
border: none;
border-bottom: 1px solid var(--Mistox-White);
outline: none;
background: transparent;
}
.big-frame .frame-item input:focus ~ label,
.big-frame .frame-item input:not(:placeholder-shown) ~ label {
top: -95px;
left: 10px;
color: var(--Mistox-Light);
font-size: 12px;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-around;
padding-bottom: 15px;
}
.sub-frame {
text-align: center;
padding: 1px 0;
}
.submit{
position: relative;
padding: 10px 20px;
color: var(--Mistox-Black);
background-color: var(--Mistox-Light);
font-size: 16px;
text-decoration: none;
text-transform: uppercase;
overflow: hidden;
transition: transform 0.3s ease, box-shadow 0.5s ease;
letter-spacing: 4px;
border: 1px solid var(--Mistox-Light);
margin: auto;
border-radius: 5px;
}
.submit:hover {
background-color: var(--Mistox-Light);
color: var(--Mistox-White);
box-shadow: 4px 3px 6px var(--Mistox-Dark);
}
.submit:active {
transform: translate( 4px, 2px );
background-color: var(--Mistox-Dark);
border: none;
color: var(--Mistox-White);
box-shadow: none;
}
ul {
list-style: none;
color: var(--Mistox-Bright);
}
@@ -0,0 +1,15 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/app",
"types": []
},
"include": [
"src/**/*.ts"
],
"exclude": [
"src/**/*.spec.ts"
]
}
+34
View File
@@ -0,0 +1,34 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"compileOnSave": false,
"compilerOptions": {
"strict": true,
"noImplicitOverride": true,
"noPropertyAccessFromIndexSignature": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"skipLibCheck": true,
"isolatedModules": true,
"experimentalDecorators": true,
"importHelpers": true,
"target": "ES2022",
"module": "preserve"
},
"angularCompilerOptions": {
"enableI18nLegacyMessageIdFormat": false,
"strictInjectionParameters": true,
"strictInputAccessModifiers": true,
"typeCheckHostBindings": true,
"strictTemplates": true
},
"files": [],
"references": [
{
"path": "./tsconfig.app.json"
},
{
"path": "./tsconfig.spec.json"
}
]
}
@@ -0,0 +1,14 @@
/* To learn more about Typescript configuration file: https://www.typescriptlang.org/docs/handbook/tsconfig-json.html. */
/* To learn more about Angular compiler options: https://angular.dev/reference/configs/angular-compiler-options. */
{
"extends": "./tsconfig.json",
"compilerOptions": {
"outDir": "./out-tsc/spec",
"types": [
"jasmine"
]
},
"include": [
"src/**/*.ts"
]
}