From 7214a64b77980838ce283c242bfad1d541ca03e2 Mon Sep 17 00:00:00 2001 From: Sheldan <5037282+Sheldan@users.noreply.github.com> Date: Fri, 5 Sep 2025 22:33:09 +0200 Subject: [PATCH] survivors: adding items and item drops adding rarities to items adding drops to enemies --- absurd-survivors/src/Enemies.ts | 36 +++++-- absurd-survivors/src/Player.ts | 34 ++++++- absurd-survivors/src/drop.ts | 36 ++++++- absurd-survivors/src/interfaces.ts | 14 +++ absurd-survivors/src/items.ts | 157 +++++++++++++++++++++++++++++ absurd-survivors/src/main.ts | 10 +- absurd-survivors/src/utils.ts | 4 + 7 files changed, 272 insertions(+), 19 deletions(-) create mode 100644 absurd-survivors/src/items.ts diff --git a/absurd-survivors/src/Enemies.ts b/absurd-survivors/src/Enemies.ts index 0fb3c46..dc74946 100644 --- a/absurd-survivors/src/Enemies.ts +++ b/absurd-survivors/src/Enemies.ts @@ -1,10 +1,11 @@ -import type {Acting, Drawable, Healthy, Placeable, Shooting} from "./interfaces.ts"; +import type {Acting, ChanceEntry, Drawable, Healthy, Placeable, Shooting} from "./interfaces.ts"; import {drawDot, moveInDirectionOf} from "./utils.ts"; import {Vector} from "./base.ts"; import {World} from "./World.ts"; import type {Projectile} from "./projectile.ts"; import {ProjectileStats, StraightProjectile} from "./projectile.ts"; -import {HealthPack, LevelDrop, MoneyDrop} from "./drop.ts"; +import {HealthPack, ItemDrop, LevelDrop, MoneyDrop} from "./drop.ts"; +import {ItemManagement} from "./items.ts"; export abstract class Enemy implements Placeable, Drawable, Acting, Healthy { protected _position: Vector; @@ -12,8 +13,11 @@ export abstract class Enemy implements Placeable, Drawable, Acting, Healthy { protected world: World; protected size: number protected status: EnemyStatus = new EnemyStatus(10); + protected drops: KillChanceTable; constructor(position: Vector) { + this.drops = new KillChanceTable(); + this.drops.addDrop( {chance: 10, creationMethod: this.spawnMoney}) this._position = position.clone(); } @@ -41,7 +45,14 @@ export abstract class Enemy implements Placeable, Drawable, Acting, Healthy { } die() { - MoneyDrop.spawnMoneyDrop(this.world, this._position); + let draw = this.drops.draw(); + if(draw) { + draw.creationMethod(this) + } + } + + spawnMoney(enemy: Enemy) { + MoneyDrop.spawnMoneyDrop(enemy.world, enemy._position); } getSize() { @@ -225,9 +236,9 @@ export class ContainerEnemy extends Enemy { super(position); this.status.health = 5; this.drops = new KillChanceTable(); - this.drops.addDrop( {chance: 50, creationMethod: this.spawnHealthPack}) - this.drops.addDrop( {chance: 50, creationMethod: this.spawnLevelUp}) - this.drops.addDrop( {chance: 10, creationMethod: this.spawnEnemy}) + ItemManagement.getItemsWithRarityFactor().forEach(drop => { + this.drops.addDrop(drop) + }) this.drops.calculateProbs() } @@ -245,7 +256,11 @@ export class ContainerEnemy extends Enemy { } die() { - this.drops.draw().creationMethod(this) + let draw = this.drops.draw(); + if(draw) { + let item = draw.creationMethod(this); + ItemDrop.spawnItemDrop(this.world, item, this._position) + } } spawnHealthPack(enemy: ContainerEnemy) { @@ -282,10 +297,6 @@ export class ContainerEnemy extends Enemy { } } -export interface ChanceEntry { - chance: number; - creationMethod: (any: any) => void; -} export class KillChanceTable { private chances: ChanceEntry[] = [] @@ -300,6 +311,9 @@ export class KillChanceTable { } draw() { + if(this.chances.length === 0) { + return undefined; + } let change = Math.random(); for (const value of this.chances) { change -= value.chance; diff --git a/absurd-survivors/src/Player.ts b/absurd-survivors/src/Player.ts index eec3b4d..02133de 100644 --- a/absurd-survivors/src/Player.ts +++ b/absurd-survivors/src/Player.ts @@ -1,4 +1,4 @@ -import type {Acting, Drawable, Healthy, Leveling, Weapon} from "./interfaces.ts"; +import type {Acting, Drawable, Healthy, Item, Leveling, Weapon} from "./interfaces.ts"; import {Vector} from "./base.ts"; import {drawDot, getCoordinatesSplit} from "./utils.ts"; @@ -8,7 +8,8 @@ export class Player implements Drawable, Acting, Healthy { private _color: string; private _status: PlayerStatus; - private _weapons: [Weapon] = [] + private _weapons: Weapon[] = [] + private _items: Item[] = [] // temp private _speed: Vector; @@ -47,6 +48,10 @@ export class Player implements Drawable, Acting, Healthy { this._weapons.push(weapon) } + addItem(item: Item) { + this._items.push(item) + } + move(direction: Vector) { this._position = this.position.add(direction) } @@ -173,6 +178,31 @@ export class PlayerStats { this._weaponRangeFactor += 0.1 } + + set speed(value: number) { + this._speed = value; + } + + set size(value: number) { + this._size = value; + } + + set health(value: number) { + this._health = value; + } + + set pullRange(value: number) { + this._pullRange = value; + } + + set weaponRange(value: number) { + this._weaponRange = value; + } + + set weaponRangeFactor(value: number) { + this._weaponRangeFactor = value; + } + get speed(): number { return this._speed; } diff --git a/absurd-survivors/src/drop.ts b/absurd-survivors/src/drop.ts index 26d31c2..8f5fd3b 100644 --- a/absurd-survivors/src/drop.ts +++ b/absurd-survivors/src/drop.ts @@ -1,7 +1,8 @@ -import type {Drop} from "./interfaces.ts"; +import type {Drop, Item} from "./interfaces.ts"; import {World} from "./World.ts"; import {drawDot, moveInDirectionOf} from "./utils.ts"; import {Vector} from "./base.ts"; +import {rarityColor} from "./items.ts"; export abstract class BasicDrop implements Drop { protected world: World; @@ -120,4 +121,37 @@ export class LevelDrop extends BasicDrop { return drop; } +} + +export class ItemDrop extends BasicDrop { + + private item: Item; + + + constructor(world: World, position: Vector, item: Item) { + super(world, position); + this.item = item; + } + + pickup() { + this.item.pickup(this.world.player, this.world) + } + + draw(ctx: CanvasRenderingContext2D) { + ctx.fillStyle = rarityColor(this.item.getRarity()) + ctx.fillText(this.item.name() + '', this._position.x, this._position.y) + } + + static spawnItemDrop(world: World, item: Item, position?: Vector) { + world.addDrop(this.createItemDrop(world, item, position)) + } + + static createItemDrop(world: World, item: Item, position?: Vector) { + if(!position) { + position = world.randomPlace() + } + let drop = new ItemDrop(world, position, item) + drop.size = 3 + return drop + } } \ No newline at end of file diff --git a/absurd-survivors/src/interfaces.ts b/absurd-survivors/src/interfaces.ts index 315f4ed..4a94adc 100644 --- a/absurd-survivors/src/interfaces.ts +++ b/absurd-survivors/src/interfaces.ts @@ -1,4 +1,7 @@ import {Vector} from "./base.ts"; +import type {Player} from "./Player.ts"; +import type {Rarity} from "./items.ts"; +import type {World} from "./World.ts"; export interface Acting { act() @@ -15,6 +18,17 @@ export interface Leveling { level() } +export interface Item { + pickup(player: Player, world: World); + name(): string + getRarity(): Rarity; +} + +export interface ChanceEntry { + chance: number; + creationMethod: (any: any) => any; +} + export interface Drop extends Drawable, Acting { pickup() } diff --git a/absurd-survivors/src/items.ts b/absurd-survivors/src/items.ts new file mode 100644 index 0000000..9b4c023 --- /dev/null +++ b/absurd-survivors/src/items.ts @@ -0,0 +1,157 @@ +import type {ChanceEntry, Item} from "./interfaces.ts"; +import {Player} from "./Player.ts"; +import {randomItem} from "./utils.ts"; +import {HomingPistol, Pistol, SpreadWeapon} from "./weapons.ts"; +import type {World} from "./World.ts"; + +export enum Rarity { + GARBAGE= 'GARBAGE', + COMMON = 'COMMON', + UNCOMMON = 'UNCOMMON', + RARE = 'RARE', + EPIC = 'EPIC', + LEGENDARY = 'LEGENDARY', + GODLY = 'GODLY' +} + +export class ItemManagement { + + private static ITEMS: Item[] = [] + + static addItem(item: Item) { + this.ITEMS.push(item) + } + + static getItemsWithRarityFactor(): ChanceEntry[] { + let items: ChanceEntry[] = [] + this.ITEMS.forEach((item) => { + items.push({chance: rarityWeight(item.getRarity()), creationMethod: () => item}) + }) + return items; + } + + static getRandomItem() { + return randomItem(this.ITEMS) + } + + static initializeItems() { + this.ITEMS.push(new SpeedUp()) + this.ITEMS.push(new HealthUp()) + this.ITEMS.push(new HomingPistolItem()) + this.ITEMS.push(new PistolItem()) + this.ITEMS.push(new SpreadWeaponItem()) + } +} + +export function rarityWeight(rarity: Rarity): number { + switch (rarity) { + case Rarity.GARBAGE: return 80; + case Rarity.COMMON: return 65; + case Rarity.UNCOMMON: return 50; + case Rarity.RARE: return 30; + case Rarity.EPIC: return 15; + case Rarity.LEGENDARY: return 5; + case Rarity.GODLY: return 1; + } +} + +export function rarityColor(rarity: Rarity): string { + switch (rarity) { + case Rarity.GARBAGE: return 'white'; + case Rarity.COMMON: return 'gray'; + case Rarity.UNCOMMON: return 'blue'; + case Rarity.RARE: return 'green'; + case Rarity.EPIC: return 'orange'; + case Rarity.LEGENDARY: return 'violett'; + case Rarity.GODLY: return 'red'; + } +} + +export abstract class BaseItem implements Item { + + constructor() { + } + + pickup(player: Player, world: World) { + player.addItem(this) + } + + abstract name(); + abstract getRarity(): Rarity; +} + +export class SpeedUp extends BaseItem { + pickup(player: Player, world: World) { + player.stats.speed += 1 + super.pickup(player, world) + } + + name() { + return 'speed' + } + + getRarity(): Rarity { + return Rarity.COMMON; + } +} + +export class HealthUp extends BaseItem { + pickup(player: Player, world: World) { + player.stats.health += 1 + super.pickup(player, world) + } + + name() { + return 'health' + } + + getRarity(): Rarity { + return Rarity.COMMON; + } +} + +export class HomingPistolItem extends BaseItem { + pickup(player: Player, world: World) { + player.addWeapon(HomingPistol.generateHomingPistol(world)) + super.pickup(player, world) + } + + name() { + return 'homingp' + } + + getRarity(): Rarity { + return Rarity.RARE; + } +} + +export class PistolItem extends BaseItem { + pickup(player: Player, world: World) { + player.addWeapon(Pistol.generatePistol(world)) + super.pickup(player, world) + } + + name() { + return 'pistol' + } + + getRarity(): Rarity { + return Rarity.RARE; + } +} + +export class SpreadWeaponItem extends BaseItem { + pickup(player: Player, world: World) { + player.addWeapon(SpreadWeapon.generateSpreadWeapon(world)) + super.pickup(player, world) + } + + name() { + return 'spreadp' + } + + getRarity(): Rarity { + return Rarity.EPIC; + } +} + diff --git a/absurd-survivors/src/main.ts b/absurd-survivors/src/main.ts index e91b3c4..818d2f9 100644 --- a/absurd-survivors/src/main.ts +++ b/absurd-survivors/src/main.ts @@ -4,10 +4,10 @@ import {docReady} from "canvas-common"; import {World} from "./World.ts"; import {Player} from "./Player.ts"; import {Vector} from "./base.ts"; -import {BasicEnemy, ContainerEnemy, Enemy, HealthEnemy, ShootingEnemy} from "./Enemies.ts"; +import {BasicEnemy, ContainerEnemy, HealthEnemy, ShootingEnemy} from "./Enemies.ts"; import {HUD} from "./ui.ts"; -import {HomingPistol, Pistol, SpreadWeapon} from "./weapons.ts"; -import {MoneyDrop} from "./drop.ts"; +import {Pistol} from "./weapons.ts"; +import {ItemManagement} from "./items.ts"; let hud: HUD; @@ -122,10 +122,10 @@ docReady(function () { }, 10_000) player.addWeapon(Pistol.generatePistol(world)) - player.addWeapon(HomingPistol.generateHomingPistol(world)) - player.addWeapon(SpreadWeapon.generateSpreadWeapon(world)) hud = new HUD(world); + ItemManagement.initializeItems() + requestAnimationFrame(updateCanvas); }) diff --git a/absurd-survivors/src/utils.ts b/absurd-survivors/src/utils.ts index cc19c47..79f2cb4 100644 --- a/absurd-survivors/src/utils.ts +++ b/absurd-survivors/src/utils.ts @@ -77,4 +77,8 @@ export function getCoordinatesSplit(amount: number) { points.push(new Vector(x, y)) } return points; +} + +export function randomItem(items: any[]) { + return items[Math.floor(Math.random() * items.length)] } \ No newline at end of file