diff --git a/angular.json b/angular.json index 31a8781..62e5120 100644 --- a/angular.json +++ b/angular.json @@ -136,7 +136,8 @@ "cli": { "schematicCollections": [ "@ionic/angular-toolkit" - ] + ], + "analytics": false }, "schematics": { "@ionic/angular-toolkit:component": { diff --git a/src/app/farmer.dto.ts b/src/app/farmer.dto.ts new file mode 100644 index 0000000..205ef84 --- /dev/null +++ b/src/app/farmer.dto.ts @@ -0,0 +1,6 @@ +export type FarmerDto = { + name: string, + level: number, + basePrice: number, + boost: number, +} diff --git a/src/app/formulas.service.spec.ts b/src/app/formulas.service.spec.ts new file mode 100644 index 0000000..dc4b389 --- /dev/null +++ b/src/app/formulas.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { FormulasService } from './formulas.service'; + +describe('FormulasService', () => { + let service: FormulasService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(FormulasService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/formulas.service.ts b/src/app/formulas.service.ts new file mode 100644 index 0000000..ff98fdd --- /dev/null +++ b/src/app/formulas.service.ts @@ -0,0 +1,13 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class FormulasService { + + constructor() { } + + getPrice(basePrice: number, growthRate: number, currentLevel: number): number { + return Math.floor(basePrice * Math.pow(growthRate, currentLevel)); + } +} diff --git a/src/app/home/home.module.ts b/src/app/home/home.module.ts index a554f01..776b3f1 100644 --- a/src/app/home/home.module.ts +++ b/src/app/home/home.module.ts @@ -5,6 +5,8 @@ import { FormsModule } from '@angular/forms'; import { HomePage } from './home.page'; import { HomePageRoutingModule } from './home-routing.module'; +import {PlantContainerComponent} from "../plant-container/plant-container.component"; +import {ProgressBarComponent} from "../progress-bar/progress-bar.component"; @NgModule({ @@ -14,6 +16,6 @@ import { HomePageRoutingModule } from './home-routing.module'; IonicModule, HomePageRoutingModule ], - declarations: [HomePage] + declarations: [HomePage, PlantContainerComponent, ProgressBarComponent] }) export class HomePageModule {} diff --git a/src/app/home/home.page.html b/src/app/home/home.page.html index 911cab7..b9542d2 100644 --- a/src/app/home/home.page.html +++ b/src/app/home/home.page.html @@ -1,20 +1,32 @@ - - - - Blank - - - - - - - Blank - - +
-
- Ready to create an app? -

Start with Ionic UI Components

+ + +
+ + + +
diff --git a/src/app/home/home.page.scss b/src/app/home/home.page.scss index 8993e7c..861b408 100644 --- a/src/app/home/home.page.scss +++ b/src/app/home/home.page.scss @@ -1,27 +1,81 @@ -#container { - text-align: center; +#game { + height: 100vh; + width: var(--game-width); + background: #edd6ff; + margin: auto; + position: relative; +} + +#header { + display: block; + width: var(--game-width); + height: calc(100vh / 6); + background: #d7a1fa; + position: relative; + color: black; + font-weight: bold; +} + +#profile-picture { position: absolute; + top: 0; left: 0; - right: 0; - top: 50%; - transform: translateY(-50%); + height: calc((100vh / 6) - 4vh); + aspect-ratio: calc(54/64); + + img { + height: calc((100vh / 6) - 4vh); + aspect-ratio: calc(54/64); + margin: 2vh; + } } -#container strong { - font-size: 20px; - line-height: 26px; +#seeds { + position: absolute; + top: 3vh; + left: calc(var(--game-width) / 2); + transform: translate(-50%, 0); } -#container p { - font-size: 16px; - line-height: 22px; - - color: #8c8c8c; - - margin: 0; +#seeds-per-second { + position: absolute; + top: 6vh; + left: calc(var(--game-width) / 2); + transform: translate(-50%, 0); } -#container a { - text-decoration: none; -} \ No newline at end of file +#fertilizer { + position: absolute; + top: 6vh; + right: 1vh; +} + +#star-dust { + position: absolute; + top: 9vh; + right: 1vh; +} + +#settings { + position: absolute; + top: 1vh; + right: 1vh; +} + +#navigation { + position: relative; + height: 3vh; + background: #9961c2; + display: grid; + grid-template-columns: repeat(5, 1fr); + + div { + height: 3vh; + width: 100%; + line-height: 3.6vh; + text-align: center; + } +} + + diff --git a/src/app/home/home.page.ts b/src/app/home/home.page.ts index 83522d5..569a0c9 100644 --- a/src/app/home/home.page.ts +++ b/src/app/home/home.page.ts @@ -1,12 +1,58 @@ -import { Component } from '@angular/core'; +import {ChangeDetectionStrategy, Component} from '@angular/core'; +import {PlayerService} from "../player.service"; +import {FormulasService} from "../formulas.service"; +import {UtilitiesService} from "../utilities.service"; +import {PlantDto} from "../plant.dto"; @Component({ selector: 'app-home', templateUrl: 'home.page.html', styleUrls: ['home.page.scss'], + // changeDetection: ChangeDetectionStrategy.OnPush, }) export class HomePage { - constructor() {} + constructor( + private playerService: PlayerService, + private formulasService: FormulasService + ) { + this.gameLoop(); + } + + getSeeds(): number { + return this.playerService.seeds; + } + + getPlants(): PlantDto[] { + return this.playerService.plants; + } + + onBuy(plant: PlantDto) { + const price = this.formulasService.getPrice(plant.basePrice, plant.growthRate, plant.level); + if (price <= this.playerService.seeds) { + plant.level += 1; + this.playerService.seeds -= price; + } + } + + gameLoop() { + + const currentTime = Date.now(); + const dt = currentTime - this.playerService.lastTime; + this.playerService.lastTime = currentTime; + + for(let plant of this.playerService.plants) { + if (plant.level > 0) { + plant.timePassed += dt; + + while (plant.timePassed >= plant.timeToGrow) { + this.playerService.seeds += plant.reward * plant.level; + plant.timePassed -= plant.timeToGrow; + } + } + } + this.playerService.save(); + requestAnimationFrame(() => this.gameLoop()); + } } diff --git a/src/app/plant-container/plant-container.component.html b/src/app/plant-container/plant-container.component.html new file mode 100644 index 0000000..1a7e665 --- /dev/null +++ b/src/app/plant-container/plant-container.component.html @@ -0,0 +1,26 @@ +
+
+ plant +
Lv. {{ plant.level }}
+
+
+ Upgrade to Lv. {{ plant.level + 1 }}
+ {{ getPrice() }} seeds +
+
+ {{ plant.name }} +
+
+ {{ getReward() }} seeds/plant +
+ +
+ + {{ getRewardTotal() }} seeds +
+
diff --git a/src/app/plant-container/plant-container.component.scss b/src/app/plant-container/plant-container.component.scss new file mode 100644 index 0000000..6fae6c5 --- /dev/null +++ b/src/app/plant-container/plant-container.component.scss @@ -0,0 +1,78 @@ +.plant-container { + position: relative; + width: 100%; + height: calc(100vh / 8); + background: #005BBB; + margin-top: 1vh; +} + +.plant-image { + position: absolute; + top: 1vh; + right: 1vh; + height: calc(100vh / 8 - 2vh); + width: calc(100vh / 8 - 2vh); + + .plant-level { + position: absolute; + bottom: 0; + left: 0; + right: 0; + height: 1.2em; + line-height: 1.6em; + text-align: center; + background: #0d0d0d66; + } +} + +.plant-progress { + position: absolute; + bottom: 1vh; + left: 1vh; + width: calc(100% - 1vh - 100vh / 8); + height: 3vh; + border: 1px solid black; +} + +.reward-total { + position: absolute; + bottom: 1vh; + right: calc(100vh / 8); + width: calc((100% - 1vh - 100vh / 8) / 3); + text-align: right; + padding-right: 1vh; + height: 3vh; + line-height: 180%; +} + +.upgrade-button { + position: absolute; + top: 1vh; + left: 1vh; + text-align: center; + height: calc(100vh / 8 - 6vh); + line-height: calc((100vh / 8 - 6vh) / 2); + background: #FFD500; + width: 20vh; + color: black; + font-weight: bold; +} + +.reward { + position: absolute; + top: calc(100vh / 16 - 2vh); + left: 22vh; + text-align: center; + height: calc(100vh / 16 - 3vh); + line-height: calc(100vh / 16 - 3vh); + width: calc(100% - 1vh - 100vh / 8 - 21vh); +} +.name { + position: absolute; + top: 1vh; + left: 22vh; + text-align: center; + height: calc(100vh / 16 - 3vh); + line-height: calc(100vh / 16 - 3vh); + width: calc(100% - 1vh - 100vh / 8 - 21vh); +} diff --git a/src/app/plant-container/plant-container.component.spec.ts b/src/app/plant-container/plant-container.component.spec.ts new file mode 100644 index 0000000..580016b --- /dev/null +++ b/src/app/plant-container/plant-container.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { PlantContainerComponent } from './plant-container.component'; + +describe('PlantContainerComponent', () => { + let component: PlantContainerComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ PlantContainerComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(PlantContainerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/plant-container/plant-container.component.ts b/src/app/plant-container/plant-container.component.ts new file mode 100644 index 0000000..371e977 --- /dev/null +++ b/src/app/plant-container/plant-container.component.ts @@ -0,0 +1,53 @@ +import {ChangeDetectionStrategy, Component, EventEmitter, Input, OnInit, Output} from '@angular/core'; +import {FormulasService} from "../formulas.service"; +import {UtilitiesService} from "../utilities.service"; +import {PlantDto} from "../plant.dto"; + +@Component({ + selector: 'app-plant-container', + templateUrl: './plant-container.component.html', + styleUrls: ['./plant-container.component.scss'], + // changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class PlantContainerComponent implements OnInit { + + @Input('plant') plant: PlantDto | undefined; + @Output('onBuy') onBuy = new EventEmitter(); + + constructor( + private formulasService: FormulasService, + private utilitiesService: UtilitiesService + ) { } + + ngOnInit() {} + + buy() { + this.onBuy.emit(this.plant); + } + + getPrice() { + if (!this.plant) return 0; + return this.formulasService.getPrice(this.plant.basePrice, this.plant.growthRate, this.plant.level); + } + + getReward() { + if (!this.plant) return 0; + return this.plant.reward; + } + + getRewardTotal() { + if (!this.plant) return 0; + return this.plant.level * this.plant.reward; + } + + progress(): number { + if (!this.plant) return 0; + return this.plant.timePassed / this.plant.timeToGrow * 100; + } + + timeLeft(): string { + if (!this.plant) return ''; + return this.utilitiesService.convertToTime(this.plant.timeToGrow - this.plant.timePassed); + } + +} diff --git a/src/app/plant.dto.ts b/src/app/plant.dto.ts new file mode 100644 index 0000000..cf8bf7f --- /dev/null +++ b/src/app/plant.dto.ts @@ -0,0 +1,9 @@ +export type PlantDto = { + name: string, + level: number, + basePrice: number, + growthRate: number, + timeToGrow: number, + timePassed: number, + reward: number, +} diff --git a/src/app/player.service.spec.ts b/src/app/player.service.spec.ts new file mode 100644 index 0000000..5355445 --- /dev/null +++ b/src/app/player.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { PlayerService } from './player.service'; + +describe('PlayerService', () => { + let service: PlayerService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(PlayerService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/app/player.service.ts b/src/app/player.service.ts new file mode 100644 index 0000000..aa9671d --- /dev/null +++ b/src/app/player.service.ts @@ -0,0 +1,102 @@ +import { Injectable } from '@angular/core'; +import {PlantDto} from "./plant.dto"; + +@Injectable({ + providedIn: 'root' +}) +export class PlayerService { + + public lastTime = Date.now(); + public seeds: number = 0; + public plants: PlantDto[] = [ + { + name: 'Plant 1', + level: 1, + basePrice: 4, + growthRate: 1.07, + timeToGrow: 600, + timePassed: 0, + reward: 1, + }, + { + name: 'Plant 2', + level: 0, + basePrice: 60, + growthRate: 1.15, + timeToGrow: 3000, + timePassed: 0, + reward: 60, + }, + { + name: 'Plant 3', + level: 0, + basePrice: 720, + growthRate: 1.14, + timeToGrow: 6000, + timePassed: 0, + reward: 540, + }, + { + name: 'Plant 4', + level: 0, + basePrice: 8640, + growthRate: 1.13, + timeToGrow: 12000, + timePassed: 0, + reward: 4320, + }, + { + name: 'Plant 5', + level: 0, + basePrice: 103680, + growthRate: 1.12, + timeToGrow: 24000, + timePassed: 0, + reward: 51840, + }, + { + name: 'Plant 6', + level: 0, + basePrice: 51200, + growthRate: 1.11, + timeToGrow: 45000, + timePassed: 0, + reward: 102400, + }, + { + name: 'Plant 7', + level: 0, + basePrice: 1000000, + growthRate: 1.106, + timeToGrow: 60000, + timePassed: 0, + reward: 204800, + }, + { + name: 'Plant 8', + level: 0, + basePrice: 5000000, + growthRate: 1.10, + timeToGrow: 60000, + timePassed: 0, + reward: 614150, + } + ]; + constructor() { + const data = localStorage.getItem('PlayerSave'); + if (data) { + const parsed = JSON.parse(data); + this.plants = parsed.plants; + this.seeds = parsed.seeds; + this.lastTime = parsed.lastTime ? parsed.lastTime : Date.now(); + } + } + + save() { + localStorage.setItem('PlayerSave', JSON.stringify({ + seeds: this.seeds, + plants: this.plants, + lastTime: this.lastTime + })); + } +} diff --git a/src/app/progress-bar/progress-bar.component.html b/src/app/progress-bar/progress-bar.component.html new file mode 100644 index 0000000..262e002 --- /dev/null +++ b/src/app/progress-bar/progress-bar.component.html @@ -0,0 +1,4 @@ +
+
+
{{ progressBarText }}
+
diff --git a/src/app/progress-bar/progress-bar.component.scss b/src/app/progress-bar/progress-bar.component.scss new file mode 100644 index 0000000..dddb247 --- /dev/null +++ b/src/app/progress-bar/progress-bar.component.scss @@ -0,0 +1,20 @@ +.progress-bar-container { + position: relative; + width: 100%; + height: 100%; + + .progress-bar-progress { + position: absolute; + right: 0; + top: 0; + height: 100%; + } + + .progress-bar-text { + position: absolute; + width: 100%; + height: 100%; + text-align: center; + line-height: 180%; + } +} diff --git a/src/app/progress-bar/progress-bar.component.spec.ts b/src/app/progress-bar/progress-bar.component.spec.ts new file mode 100644 index 0000000..96e97e1 --- /dev/null +++ b/src/app/progress-bar/progress-bar.component.spec.ts @@ -0,0 +1,24 @@ +import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing'; +import { IonicModule } from '@ionic/angular'; + +import { ProgressBarComponent } from './progress-bar.component'; + +describe('ProgressBarComponent', () => { + let component: ProgressBarComponent; + let fixture: ComponentFixture; + + beforeEach(waitForAsync(() => { + TestBed.configureTestingModule({ + declarations: [ ProgressBarComponent ], + imports: [IonicModule.forRoot()] + }).compileComponents(); + + fixture = TestBed.createComponent(ProgressBarComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + })); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/app/progress-bar/progress-bar.component.ts b/src/app/progress-bar/progress-bar.component.ts new file mode 100644 index 0000000..8d5b4fe --- /dev/null +++ b/src/app/progress-bar/progress-bar.component.ts @@ -0,0 +1,20 @@ +import {ChangeDetectionStrategy, Component, Input, OnInit} from '@angular/core'; + +@Component({ + selector: 'app-progress-bar', + templateUrl: './progress-bar.component.html', + styleUrls: ['./progress-bar.component.scss'], + // changeDetection: ChangeDetectionStrategy.OnPush, +}) +export class ProgressBarComponent implements OnInit { + + @Input('fill') fill: number = 0; + @Input('progressBarText') progressBarText: string = ''; + @Input('backgroundColor') backgroundColor: string = '#1a1a1a'; + @Input('fillColor') fillColor: string = '#2dd36f'; + + constructor() { } + + ngOnInit() {} + +} diff --git a/src/app/utilities.service.ts b/src/app/utilities.service.ts new file mode 100644 index 0000000..170879c --- /dev/null +++ b/src/app/utilities.service.ts @@ -0,0 +1,34 @@ +import { Injectable } from '@angular/core'; + +@Injectable({ + providedIn: 'root' +}) +export class UtilitiesService { + + constructor() { } + + convertToTime(num: number) { + num = Math.round(num / 1000); + const days = Math.floor(num / 60 / 60 / 24); + num -= days * 60 * 60 * 24; + const hours = Math.floor(num / 60 / 60); + num -= hours * 60 * 60; + const minutes = Math.floor(num / 60); + num -= minutes * 60; + const seconds = num; + + if (days) { + return days + ' ' + (hours > 9 ? hours : '0' + hours) + ':' + (minutes > 9 ? minutes : '0' + minutes) + ':' + (seconds > 9 ? seconds : '0' + seconds); + } + + if (hours) { + return (hours > 9 ? hours : '0' + hours) + ':' + (minutes > 9 ? minutes : '0' + minutes) + ':' + (seconds > 9 ? seconds : '0' + seconds); + } + + if (minutes) { + return (minutes > 9 ? minutes : '0' + minutes) + ':' + (seconds > 9 ? seconds : '0' + seconds); + } + + return seconds + ' sec'; + } +} diff --git a/src/app/utils.service.spec.ts b/src/app/utils.service.spec.ts new file mode 100644 index 0000000..d7950f3 --- /dev/null +++ b/src/app/utils.service.spec.ts @@ -0,0 +1,16 @@ +import { TestBed } from '@angular/core/testing'; + +import { UtilitiesService } from './utilities.service'; + +describe('UtilsService', () => { + let service: UtilitiesService; + + beforeEach(() => { + TestBed.configureTestingModule({}); + service = TestBed.inject(UtilitiesService); + }); + + it('should be created', () => { + expect(service).toBeTruthy(); + }); +}); diff --git a/src/assets/pfp-default.kra b/src/assets/pfp-default.kra new file mode 100644 index 0000000..3a93bc5 Binary files /dev/null and b/src/assets/pfp-default.kra differ diff --git a/src/assets/pfp-default.png b/src/assets/pfp-default.png new file mode 100644 index 0000000..0339957 Binary files /dev/null and b/src/assets/pfp-default.png differ diff --git a/src/assets/plant.png b/src/assets/plant.png new file mode 100644 index 0000000..180a862 Binary files /dev/null and b/src/assets/plant.png differ diff --git a/src/global.scss b/src/global.scss index d854de8..e2775aa 100644 --- a/src/global.scss +++ b/src/global.scss @@ -24,3 +24,20 @@ @import "~@ionic/angular/css/text-alignment.css"; @import "~@ionic/angular/css/text-transformation.css"; @import "~@ionic/angular/css/flex-utils.css"; + +@import url('https://fonts.googleapis.com/css2?family=Solitreo&display=swap'); +:root { + --game-width: calc(100vh / 16 * 9) +} + +body { + overflow: hidden; +} + +* { + font-family: 'Solitreo', cursive; + font-size: 2vh; + -webkit-user-select: none; /* Safari */ + -ms-user-select: none; /* IE 10 and IE 11 */ + user-select: none; /* Standard syntax */ +} diff --git a/src/index.html b/src/index.html index 3b0aae1..4c514c7 100644 --- a/src/index.html +++ b/src/index.html @@ -3,7 +3,7 @@ - Ionic App + Lily - Idle