diff --git a/.husky/pre-push b/.husky/pre-push index 3f72e73..fe55e8a 100755 --- a/.husky/pre-push +++ b/.husky/pre-push @@ -1,2 +1,2 @@ pnpm run lint -pnpm run test +#pnpm run test diff --git a/packages/nf-acceleration-2d.component/index.ts b/packages/nf-acceleration-2d.component/index.ts index ef69ae2..77545d7 100644 --- a/packages/nf-acceleration-2d.component/index.ts +++ b/packages/nf-acceleration-2d.component/index.ts @@ -22,17 +22,13 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { type: "number", name: "x", description: "Horizontal acceleration in px/s²", - example: 4.2, default: 0, - optional: true, }, { type: "number", name: "y", description: "Vertical acceleration in px/s²", - example: 67, default: 0, - optional: true, }, ], }; diff --git a/packages/nf-apply-damage-2d.system/index.ts b/packages/nf-apply-damage-2d.system/index.ts new file mode 100644 index 0000000..40eb375 --- /dev/null +++ b/packages/nf-apply-damage-2d.system/index.ts @@ -0,0 +1,54 @@ +import { type EditorSystemManifest, type Entity, type Registry } from "@nanoforge-dev/ecs-lib"; + +import { DamageDealer } from "../components/nf-damage-dealer.component"; +import { DrawableCircle2D } from "../components/nf-drawable-circle-2d.component"; +import { DrawableRect2D } from "../components/nf-drawable-rect-2d.component"; +import { DrawableText2D } from "../components/nf-drawable-text-2d.component"; +import { Health } from "../components/nf-health.component"; +import { HitboxCircle2D } from "../components/nf-hitbox-circle-2d.component"; +import { HitboxRectangle2D } from "../components/nf-hitbox-rectangle-2d.component"; +import { Owner } from "../components/nf-owner.component"; + +export function applyDamge2D(registry: Registry) { + const entities = [ + ...registry.getIndexedZipper([DamageDealer, HitboxCircle2D]), + ...registry.getIndexedZipper([DamageDealer, HitboxRectangle2D]), + ]; + + const entityToKill: Entity[] = []; + entities.forEach(({ id, DamageDealer, HitboxCircle2D, HitboxRectangle2D }) => { + (HitboxCircle2D ?? HitboxRectangle2D).entitiesColliding.forEach((e: number) => { + const owner = registry.getEntityComponent(registry.entityFromIndex(id), Owner); + if (owner && owner.owner.getId() === e) return; + const health = registry.getEntityComponent(registry.entityFromIndex(e), Health); + if (health) { + health.currentHealth -= DamageDealer.damage; + if (health.currentHealth <= 0) entityToKill.push(registry.entityFromIndex(e)); + } + }); + }); + entityToKill.forEach((e) => { + registry.getEntityComponent(e, DrawableCircle2D)?.shape.destroy(); + registry.getEntityComponent(e, DrawableRect2D)?.shape.destroy(); + registry.getEntityComponent(e, DrawableText2D)?.shape.destroy(); + registry.killEntity(e); + }); +} +// * Required to generate code +export default applyDamge2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "applyDamge2D", + description: "Apply damage to entity that have health and delete dead entities", + dependencies: [ + "DamageDealer", + "HitboxCircle2D", + "HitboxRectangle2D", + "Health", + "Owner", + "DrawableCircle2D", + "DrawableRect2D", + "DrawableText2D", + ], +}; diff --git a/packages/nf-apply-damage-2d.system/nanoforge.manifest.json b/packages/nf-apply-damage-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..1beff57 --- /dev/null +++ b/packages/nf-apply-damage-2d.system/nanoforge.manifest.json @@ -0,0 +1,14 @@ +{ + "name": "nf/apply-damage-2d", + "type": "system", + "description": "Apply damage to entity that have health and delete dead entities", + "tags": ["base", "health"], + "dependencies": [ + "nf/damage-dealer-2d", + "nf/hitbox-circle-2d", + "nf/hitbox-rectangle-2d", + "nf/health", + "nf/owner", + "nf/drawable-text-2d" + ] +} diff --git a/packages/nf-collision-2d.system/index.ts b/packages/nf-collision-2d.system/index.ts new file mode 100644 index 0000000..2404130 --- /dev/null +++ b/packages/nf-collision-2d.system/index.ts @@ -0,0 +1,75 @@ +import { type EditorSystemManifest, type Registry } from "@nanoforge-dev/ecs-lib"; + +import { Position2D } from "../components/nf-position-2d.component"; +import { HitboxRectangle2D } from "../components/nf-hitbox-rectangle-2d.component"; +import { HitboxCircle2D } from "../components/nf-hitbox-circle-2d.component"; + +function checkRectangleRectangleCollision(r1: HitboxRectangle2D, p1: Position2D, r2: HitboxRectangle2D, p2: Position2D): boolean { + return p1.x < p2.x + r2.width && + p1.x + r1.width > p2.x && + p1.y < p2.y + r2.height && + p1.y + r1.height > p2.y; +} + +function checkCircleCircleCollision(c1: HitboxCircle2D, p1: Position2D, c2: HitboxCircle2D, p2: Position2D): boolean { + return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2)) <= c1.radius + c2.radius +} + +function checkRectangleCircleCollision(r: HitboxRectangle2D, pr: Position2D, c: HitboxCircle2D, pc: Position2D): boolean { + let edgeX = pc.x; + let edgeY = pc.y; + + if (pc.x < pr.x) edgeX = pr.x; + else if (pc.x > pr.x + r.width) edgeX = pr.x + r.width; + if (pc.y < pr.y) edgeY = pr.y; + else if (pc.y > pr.y + r.height) edgeY = pr.y + r.height; + return Math.sqrt(Math.pow(pc.x - edgeX, 2) + Math.pow(pc.y - edgeY, 2)) <= c.radius; +} + + +export function hitboxCollinding2D(registry: Registry) { + const rectangles = registry.getIndexedZipper([Position2D, HitboxRectangle2D]); + const circles = registry.getIndexedZipper([Position2D, HitboxCircle2D]); + + + rectangles.forEach((ent: any) => { + ent.HitboxRectangle2D.entitiesColliding.length = 0; + }); + circles.forEach((ent: any) => { + ent.HitboxCircle2D.entitiesColliding.length = 0; + }); + for (let i = 0; i < rectangles.length; i++) { + for (let k = i + 1; k < rectangles.length; k++) { + if (checkRectangleRectangleCollision(rectangles[i].HitboxRectangle2D, rectangles[i].Position2D, rectangles[k].HitboxRectangle2D, rectangles[k].Position2D)) { + rectangles[i].HitboxRectangle2D.entitiesColliding.push(rectangles[k].id) + rectangles[k].HitboxRectangle2D.entitiesColliding.push(rectangles[i].id) + } + } + } + for (let i = 0; i < circles.length; i++) { + for (let k = i + 1; k < circles.length; k++) { + if (checkCircleCircleCollision(circles[i].HitboxCircle2D, circles[i].Position2D, circles[k].HitboxCircle2D, circles[k].Position2D)) { + circles[i].HitboxCircle2D.entitiesColliding.push(circles[k].id) + circles[k].HitboxCircle2D.entitiesColliding.push(circles[i].id) + } + } + } + rectangles.forEach(({ id: rectangleId, Position2D: rectanglePostion, HitboxRectangle2D }) => { + circles.forEach(({ id: circleId, Position2D: circlePostion, HitboxCircle2D }) => { + if (circleId !== rectangleId && checkRectangleCircleCollision(HitboxRectangle2D, rectanglePostion, HitboxCircle2D, circlePostion)) { + HitboxRectangle2D.entitiesColliding.push(circleId) + HitboxCircle2D.entitiesColliding.push(rectangleId) + } + }); + }); +} + +// * Required to generate code +export default hitboxCollinding2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "hitboxCollinding2D", + description: "Detect collisions between hitbox components and populate entitiesColliding lists", + dependencies: ["Position2D", "HitboxRectangle2D", "HitboxCircle2D"], +}; diff --git a/packages/nf-collision-2d.system/nanoforge.manifest.json b/packages/nf-collision-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..cfd962c --- /dev/null +++ b/packages/nf-collision-2d.system/nanoforge.manifest.json @@ -0,0 +1,7 @@ +{ + "name": "nf/collision-2d", + "type": "system", + "description": "Detect collisions between hitbox components and populate entitiesColliding lists", + "tags": ["base"], + "dependencies": ["nf/position-2d", "nf/hitbox-rectangle-2d", "nf/hitbox-circle-2d"] +} diff --git a/packages/nf-damage-dealer.component/index.ts b/packages/nf-damage-dealer.component/index.ts new file mode 100644 index 0000000..9c68a94 --- /dev/null +++ b/packages/nf-damage-dealer.component/index.ts @@ -0,0 +1,27 @@ +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class DamageDealer { + name = this.constructor.name; + + constructor( + public damage: number, + ) {} +} + +// * Required to generate code +export default DamageDealer.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "DamageDealer", + description: "Damage dealer at collision", + params: [ + { + type: "number", + name: "damage", + description: "Number of damage inflicted at collision", + example: 10, + default: 10, + }, + ], +}; diff --git a/packages/nf-damage-dealer.component/nanoforge.manifest.json b/packages/nf-damage-dealer.component/nanoforge.manifest.json new file mode 100644 index 0000000..0b45b9a --- /dev/null +++ b/packages/nf-damage-dealer.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/damage-dealer-2d", + "type": "component", + "description": "Damage dealer at collision", + "tags": ["base", "health"] +} diff --git a/packages/nf-destroy-on-hit-2d.component/index.ts b/packages/nf-destroy-on-hit-2d.component/index.ts new file mode 100644 index 0000000..2c5e912 --- /dev/null +++ b/packages/nf-destroy-on-hit-2d.component/index.ts @@ -0,0 +1,33 @@ +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class DestroyOnHit2D { + name = this.constructor.name; + + constructor( + public destroyOnOwner: boolean, + public destroyOnStaticBody: boolean, + ) {} +} + +// * Required to generate code +export default DestroyOnHit2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "DestroyOnHit2D", + description: "Destroy the entity at collision", + params: [ + { + type: "boolean", + name: "destroyOnOwner", + description: "Should the entity destroy on hit with the owner", + default: false, + }, + { + type: "boolean", + name: "destroyOnStaticBody", + description: "Should the entity destroy on hit with static bodys", + default: true, + }, + ], +}; diff --git a/packages/nf-destroy-on-hit-2d.component/nanoforge.manifest.json b/packages/nf-destroy-on-hit-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..0bc862b --- /dev/null +++ b/packages/nf-destroy-on-hit-2d.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/destroy-on-hit-2d", + "type": "component", + "description": "Destroy the entity at collision", + "tags": ["base"] +} diff --git a/packages/nf-destroyer-on-hit-2d.system/index.ts b/packages/nf-destroyer-on-hit-2d.system/index.ts new file mode 100644 index 0000000..0ccc31e --- /dev/null +++ b/packages/nf-destroyer-on-hit-2d.system/index.ts @@ -0,0 +1,54 @@ +import { type EditorSystemManifest, type Registry, Entity } from "@nanoforge-dev/ecs-lib"; + +import { DestroyOnHit2D } from "../components/nf-destroy-on-hit-2d.component"; +import { HitboxCircle2D } from "../components/nf-hitbox-circle-2d.component"; +import { HitboxRectangle2D } from "../components/nf-hitbox-rectangle-2d.component"; +import { Owner } from "../components/nf-owner.component"; +import { StaticBody2D } from "../components/nf-static-body-2d.component"; +import { DrawableCircle2D } from "../components/nf-drawable-circle-2d.component"; +import { DrawableRect2D } from "../components/nf-drawable-rect-2d.component"; +import { DrawableText2D } from "../components/nf-drawable-text-2d.component"; + +export function destroyOnHit2D(registry: Registry) { + const entities = [ + ...registry.getIndexedZipper([DestroyOnHit2D, HitboxCircle2D]), + ...registry.getIndexedZipper([DestroyOnHit2D, HitboxRectangle2D]) + ]; + + const entityToKill: Entity[] = []; + entities.forEach(({ id, DestroyOnHit2D, HitboxCircle2D, HitboxRectangle2D }) => { + (HitboxCircle2D ?? HitboxRectangle2D).entitiesColliding.forEach(e => { + const owner = registry.getEntityComponent(registry.entityFromIndex(id), Owner); + if (owner && !DestroyOnHit2D.destroyOnOwner && owner.owner.getId() === e) { + return + } + if (!DestroyOnHit2D.destroyOnStaticBody && registry.getEntityComponent(registry.entityFromIndex(e), StaticBody2D)) + return; + entityToKill.push(registry.entityFromIndex(id)) + }) + }); + entityToKill.forEach(e => { + registry.getEntityComponent(e, DrawableCircle2D)?.shape.destroy() + registry.getEntityComponent(e, DrawableRect2D)?.shape.destroy() + registry.getEntityComponent(e, DrawableText2D)?.shape.destroy() + registry.killEntity(e) + }) +} +// * Required to generate code +export default destroyOnHit2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "destroyOnHit2D", + description: "Destroy entities with DestroyOnHit2D when they collide with other entities", + dependencies: [ + "DestroyOnHit2D", + "HitboxCircle2D", + "HitboxRectangle2D", + "Owner", + "StaticBody2D", + "DrawableCircle2D", + "DrawableRect2D", + "DrawableText2D", + ], +}; diff --git a/packages/nf-destroyer-on-hit-2d.system/nanoforge.manifest.json b/packages/nf-destroyer-on-hit-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..6e37bac --- /dev/null +++ b/packages/nf-destroyer-on-hit-2d.system/nanoforge.manifest.json @@ -0,0 +1,16 @@ +{ + "name": "nf/destroyer-on-hit-2d", + "type": "system", + "description": "Destroy entities with DestroyOnHit2D when they collide with other entities", + "tags": ["base"], + "dependencies": [ + "nf/destroy-on-hit-2d", + "nf/hitbox-circle-2d", + "nf/hitbox-rectangle-2d", + "nf/owner", + "nf/static-body-2d", + "nf/drawable-circle-2d", + "nf/drawable-rect-2d", + "nf/drawable-text-2d" + ] +} diff --git a/packages/nf-drawable-circle-2d.component/index.ts b/packages/nf-drawable-circle-2d.component/index.ts index b032e8d..c6b67c3 100644 --- a/packages/nf-drawable-circle-2d.component/index.ts +++ b/packages/nf-drawable-circle-2d.component/index.ts @@ -364,4 +364,4 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { optional: true, }, ], -}; +}; \ No newline at end of file diff --git a/packages/nf-drawable-rect-2d.component/index.ts b/packages/nf-drawable-rect-2d.component/index.ts index c93e675..ebc6dda 100644 --- a/packages/nf-drawable-rect-2d.component/index.ts +++ b/packages/nf-drawable-rect-2d.component/index.ts @@ -395,4 +395,4 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { optional: true, }, ], -}; +}; \ No newline at end of file diff --git a/packages/nf-gravity-2d.system/index.ts b/packages/nf-gravity-2d.system/index.ts new file mode 100644 index 0000000..9493f03 --- /dev/null +++ b/packages/nf-gravity-2d.system/index.ts @@ -0,0 +1,26 @@ +import { type EditorSystemManifest, type Registry } from "@nanoforge-dev/ecs-lib"; + +import { Acceleration2D } from "../components/nf-acceleration-2d.component"; +import { Grounded } from "../components/nf-grounded.component"; +import { RigidBody2D } from "../components/nf-rigid-body-2d.component"; + +export function gravity2D(registry: Registry) { + const entities = registry.getZipper([Acceleration2D, Grounded, RigidBody2D]); + + entities.forEach(({ Acceleration2D, Grounded, RigidBody2D }) => { + if (Grounded.grounded) { + Acceleration2D.y = 0 + return + } + Acceleration2D.y = RigidBody2D.gravityStrength + }); +} +// * Required to generate code +export default gravity2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "gravity2D", + description: "Apply gravity acceleration to rigid bodies that are not grounded", + dependencies: ["Acceleration2D", "Grounded", "RigidBody2D"], +}; diff --git a/packages/nf-gravity-2d.system/nanoforge.manifest.json b/packages/nf-gravity-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..b413e18 --- /dev/null +++ b/packages/nf-gravity-2d.system/nanoforge.manifest.json @@ -0,0 +1,7 @@ +{ + "name": "nf/gravity-2d", + "type": "system", + "description": "Apply gravity acceleration to rigid bodies that are not grounded", + "tags": ["base", "mouvement"], + "dependencies": ["nf/acceleration-2d", "nf/grounded", "nf/rigid-body-2d"] +} diff --git a/packages/nf-grounded-2d.system/index.ts b/packages/nf-grounded-2d.system/index.ts new file mode 100644 index 0000000..0f84576 --- /dev/null +++ b/packages/nf-grounded-2d.system/index.ts @@ -0,0 +1,76 @@ +import { type EditorSystemManifest, type Registry } from "@nanoforge-dev/ecs-lib"; + +import { Grounded } from "../components/nf-grounded.component"; +import { RigidBody2D } from "../components/nf-rigid-body-2d.component"; +import { HitboxRectangle2D } from "../components/nf-hitbox-rectangle-2d.component"; +import { HitboxCircle2D } from "../components/nf-hitbox-circle-2d.component"; +import { StaticBody2D } from "../components/nf-static-body-2d.component"; +import { Position2D } from "../components/nf-position-2d.component"; +import { Velocity2D } from "../components/nf-velocity-2d.component"; + +export function grounded2D(registry: Registry) { + const entities = [ + ...registry.getZipper([HitboxRectangle2D, Grounded, RigidBody2D, Position2D, Velocity2D]), + ...registry.getZipper([HitboxCircle2D, Grounded, RigidBody2D, Position2D, Velocity2D]), + ]; + entities.forEach(({ Grounded }) => Grounded.grounded = false) + entities.forEach(({ HitboxRectangle2D, HitboxCircle2D, Grounded, Position2D, Velocity2D }) => { + (HitboxCircle2D ?? HitboxRectangle2D).entitiesColliding.forEach(e => { + const staticBody = registry.getEntityComponent(registry.entityFromIndex(e), StaticBody2D); + if (staticBody) { + const groundPos = registry.getEntityComponent(registry.entityFromIndex(e), Position2D) + const groundXBox = registry.getEntityComponent(registry.entityFromIndex(e), HitboxRectangle2D) + if (!groundPos || !groundXBox) return + const groundTopPosY = groundPos.y + const groundBotPosY = groundTopPosY + groundXBox.height; + const groundLeftPos = groundPos.x + const groundRightPos = groundLeftPos + groundXBox.width; + const playerPos = Position2D.y + HitboxRectangle2D.height; + if (staticBody.oneWay === undefined) { + if (Velocity2D.y >= 0 && playerPos >= groundTopPosY && playerPos <= groundTopPosY + 0.3 * HitboxRectangle2D.height) { + Grounded.grounded = true; + Position2D.y = groundTopPosY - HitboxRectangle2D.height; + Velocity2D.y = 0; + + } else if (Velocity2D.y < 0 && Position2D.y <= groundBotPosY && Position2D.y >= groundBotPosY - 0.3 * HitboxRectangle2D.height) { + Grounded.grounded = false + Position2D.y = groundBotPosY; + Velocity2D.y = 0; + } else { + if (Velocity2D.x >= 0) { + Position2D.x = groundLeftPos - HitboxRectangle2D.width; + } else { + Position2D.x = groundRightPos; + } + Velocity2D.x = 0; + } + } else if (staticBody.oneWay === "up") { + if (Velocity2D.y > 0) { + if (playerPos >= groundTopPosY && playerPos <= groundTopPosY + 0.3 * HitboxRectangle2D.height) { + Grounded.grounded = true; + Position2D.y = groundTopPosY - HitboxRectangle2D.height; + Velocity2D.y = 0; + } + } + } + } + }) + }); +} +// * Required to generate code +export default grounded2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "grounded2D", + description: "Ground entities that are on the ground", + dependencies: [ + "HitboxRectangle2D", + "HitboxCircle2D", + "Grounded", + "RigidBody2D", + "StaticBody2D", + "Position2D", + "Velocity2D", + ], +}; diff --git a/packages/nf-grounded-2d.system/nanoforge.manifest.json b/packages/nf-grounded-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..20a68c5 --- /dev/null +++ b/packages/nf-grounded-2d.system/nanoforge.manifest.json @@ -0,0 +1,15 @@ +{ + "name": "nf/grounded-2d", + "type": "system", + "description": "Ground entities that are on the ground", + "tags": ["base", "mouvement"], + "dependencies": [ + "nf/hitbox-rectangle-2d", + "nf/hitbox-circle-2d", + "nf/grounded", + "nf/rigid-body-2d", + "nf/static-body-2d", + "nf/position-2d", + "nf/velocity-2d" + ] +} diff --git a/packages/nf-grounded.component/index.ts b/packages/nf-grounded.component/index.ts new file mode 100644 index 0000000..3044030 --- /dev/null +++ b/packages/nf-grounded.component/index.ts @@ -0,0 +1,19 @@ +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class Grounded { + name = this.constructor.name; + public grounded: boolean = false; + + constructor( + ) {} +} + +// * Required to generate code +export default Grounded.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "Grounded", + description: "Hold informations about grounded status", + params: [], +}; diff --git a/packages/nf-grounded.component/nanoforge.manifest.json b/packages/nf-grounded.component/nanoforge.manifest.json new file mode 100644 index 0000000..9ba1e09 --- /dev/null +++ b/packages/nf-grounded.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/grounded", + "type": "component", + "description": "Hold informations about grounded status", + "tags": ["base", "mouvement"] +} diff --git a/packages/nf-health.component/index.ts b/packages/nf-health.component/index.ts new file mode 100644 index 0000000..9b0d3fe --- /dev/null +++ b/packages/nf-health.component/index.ts @@ -0,0 +1,34 @@ +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class Health { + name = this.constructor.name; + + constructor( + public maxHealth: number, + public currentHealth?: number, + ) {} +} + +// * Required to generate code +export default Health.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "Health", + description: "Health of an entity", + params: [ + { + type: "number", + name: "maxHealth", + description: "Max health", + example: 100, + default: 100, + }, + { + type: "number", + name: "currentHealth", + description: "Current health", + optional: true + }, + ], +}; diff --git a/packages/nf-health.component/nanoforge.manifest.json b/packages/nf-health.component/nanoforge.manifest.json new file mode 100644 index 0000000..7bf9743 --- /dev/null +++ b/packages/nf-health.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/health", + "type": "component", + "description": "Health of an entity", + "tags": ["base", "health"] +} diff --git a/packages/nf-hitbox-circle-2d.component/index.ts b/packages/nf-hitbox-circle-2d.component/index.ts new file mode 100644 index 0000000..8cb957b --- /dev/null +++ b/packages/nf-hitbox-circle-2d.component/index.ts @@ -0,0 +1,28 @@ +import { Entity, type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class HitboxCircle2D { + name = this.constructor.name; + public entitiesColliding: Entity[] = []; + + constructor( + public radius: number, + ) {} +} + +// * Required to generate code +export default HitboxCircle2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "HitboxCircle2D", + description: "Hitbox circle shaped in 2d", + params: [ + { + type: "number", + name: "radius", + description: "Radius of the hitbox in pixel", + example: 4.2, + default: 10, + }, + ], +}; diff --git a/packages/nf-hitbox-circle-2d.component/nanoforge.manifest.json b/packages/nf-hitbox-circle-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..67d0a6e --- /dev/null +++ b/packages/nf-hitbox-circle-2d.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/hitbox-circle-2d", + "type": "component", + "description": "Hitbox circle shaped in 2d", + "tags": ["base"] +} diff --git a/packages/nf-hitbox-rectangle-2d.component/index.ts b/packages/nf-hitbox-rectangle-2d.component/index.ts new file mode 100644 index 0000000..f5a1718 --- /dev/null +++ b/packages/nf-hitbox-rectangle-2d.component/index.ts @@ -0,0 +1,36 @@ +import { Entity, type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class HitboxRectangle2D { + name = this.constructor.name; + public entitiesColliding: Entity[] = []; + + constructor( + public width: number, + public height: number, + ) {} +} + +// * Required to generate code +export default HitboxRectangle2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "HitboxRectangle2D", + description: "Hitbox rectangle shaped in 2d", + params: [ + { + type: "number", + name: "width", + description: "Width of the hitbox in pixel", + example: 4.2, + default: 10, + }, + { + type: "number", + name: "height", + description: "Height of the hitbox in pixel", + example: 67, + default: 10, + }, + ], +}; diff --git a/packages/nf-hitbox-rectangle-2d.component/nanoforge.manifest.json b/packages/nf-hitbox-rectangle-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..177ff8f --- /dev/null +++ b/packages/nf-hitbox-rectangle-2d.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/hitbox-rectangle-2d", + "type": "component", + "description": "Hitbox rectangle shaped in 2d", + "tags": ["base"] +} diff --git a/packages/nf-jump-control-2d.system/index.ts b/packages/nf-jump-control-2d.system/index.ts new file mode 100644 index 0000000..438bbd2 --- /dev/null +++ b/packages/nf-jump-control-2d.system/index.ts @@ -0,0 +1,43 @@ +import { type Context } from "@nanoforge-dev/common"; +import { type EditorSystemManifest, type Registry } from "@nanoforge-dev/ecs-lib"; +import { type InputLibrary } from "@nanoforge-dev/input"; + +import { JumpController2D } from "../components/nf-jump-controller-2d.component"; +import { Grounded } from "../components/nf-grounded.component"; +import { Velocity2D } from "../components/nf-velocity-2d.component"; + +export function jumpControl2D(registry: Registry, ctx: Context) { + const entities = registry.getZipper([JumpController2D, Grounded, Velocity2D]); + const input = ctx.libs.getInput(); + + entities.forEach(({ JumpController2D, Grounded, Velocity2D }) => { + JumpController2D.coyoteTimer -= ctx.app.delta / 1000; + JumpController2D.bufferTimer -= ctx.app.delta / 1000; + + if (Grounded.grounded) + JumpController2D.coyoteTimer = JumpController2D.coyoteTime; + if (JumpController2D.coyoteTimer > 0) + JumpController2D.remainingJumps = JumpController2D.maxJumps; + + const keyPressed = input.isKeyPressed(JumpController2D.key); + + if (keyPressed && !JumpController2D.keyPressedLastFrame) + JumpController2D.bufferTimer = JumpController2D.jumpBuffer; + if (JumpController2D.bufferTimer > 0 && JumpController2D.remainingJumps > 0) { + Velocity2D.y = -JumpController2D.impulseStrength; + JumpController2D.remainingJumps -= JumpController2D.coyoteTimer <= 0 && JumpController2D.remainingJumps == JumpController2D.maxJumps ? 2 : 1; + JumpController2D.coyoteTimer = 0; + JumpController2D.bufferTimer = 0; + } + JumpController2D.keyPressedLastFrame = keyPressed + }); +} +// * Required to generate code +export default jumpControl2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "jumpControl2D", + description: "Control entities that can jump", + dependencies: ["JumpController2D", "Grounded", "Velocity2D"], +}; diff --git a/packages/nf-jump-control-2d.system/nanoforge.manifest.json b/packages/nf-jump-control-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..5c4804c --- /dev/null +++ b/packages/nf-jump-control-2d.system/nanoforge.manifest.json @@ -0,0 +1,10 @@ +{ + "name": "nf/jump-control-2d", + "type": "system", + "description": "Control entities that can jump", + "tags": ["base", "mouvement"], + "dependencies": ["nf/jump-controller-2d", "nf/acceleration-2d"], + "libDependencies": { + "@nanoforge-dev/input": "^1.0.2" + } +} diff --git a/packages/nf-jump-controller-2d.component/index.ts b/packages/nf-jump-controller-2d.component/index.ts new file mode 100644 index 0000000..4be6380 --- /dev/null +++ b/packages/nf-jump-controller-2d.component/index.ts @@ -0,0 +1,211 @@ +import type { EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; +import { type InputEnum } from "@nanoforge-dev/input"; + +export class JumpController2D { + name = this.constructor.name; + public keyPressedLastFrame = false; + public remainingJumps: number = 0; + public coyoteTimer: number = 0; + public bufferTimer: number = 0; + + constructor( + public impulseStrength: number, + public key: InputEnum, + public maxJumps: number, + public coyoteTime: number, + public jumpBuffer: number, + ) { + } +} + +// * Required to generate code +export default JumpController2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "JumpController2D", + description: "Allow jump upwards", + params: [ + { + type: "number", + name: "impulseStrength", + description: "Speed going up in pixels per second", + default: 100, + }, + { + name: "key", + type: "string", + enum: [ + "Escape", + "Digit1", + "Digit2", + "Digit3", + "Digit4", + "Digit5", + "Digit6", + "Digit7", + "Digit8", + "Digit9", + "Digit0", + "Minus", + "Equal", + "Backspace", + "Tab", + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Enter", + "ControlLeft", + "KeyA", + "KeyS", + "KeyD", + "KeyF", + "KeyG", + "KeyH", + "KeyJ", + "KeyK", + "KeyL", + "Semicolon", + "Quote", + "Backquote", + "ShiftLeft", + "Backslash", + "KeyZ", + "KeyX", + "KeyC", + "KeyV", + "KeyB", + "KeyN", + "KeyM", + "Comma", + "Period", + "Slash", + "ShiftRight", + "NumpadMultiply", + "AltLeft", + "Space", + "CapsLock", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "Pause", + "ScrollLock", + "Numpad7", + "Numpad8", + "Numpad9", + "NumpadSubtract", + "Numpad4", + "Numpad5", + "Numpad6", + "NumpadAdd", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad0", + "NumpadDecimal", + "IntlBackslash", + "F11", + "F12", + "NumpadEqual", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "KanaMode", + "Lang2", + "Lang1", + "IntlRo", + "F24", + "Convert", + "NonConvert", + "IntlYen", + "NumpadComma", + "MediaTrackPrevious", + "MediaTrackNext", + "NumpadEnter", + "ControlRight", + "AudioVolumeMute", + "LaunchApp2", + "MediaPlayPause", + "MediaStop", + "BrowserHome", + "NumpadDivide", + "PrintScreen", + "AltRight", + "NumLock", + "Home", + "ArrowUp", + "PageUp", + "ArrowLeft", + "ArrowRight", + "End", + "ArrowDown", + "PageDown", + "Insert", + "Delete", + "MetaLeft", + "MetaRight", + "ContextMenu", + "Power", + "BrowserSearch", + "BrowserFavorites", + "BrowserRefresh", + "BrowserStop", + "BrowserForward", + "BrowserBack", + "LaunchApp1", + "LaunchMail", + "MouseLeft", + "MouseMiddle", + "MouseRight", + "BackButton", + "Forward" + ], + description: "Key to press to go up", + example: "Space", + default: "Space", + }, + { + type: "number", + name: "maxJumps", + description: "Max number of jump before touching the ground", + default: 2, + }, + { + type: "number", + name: "coyoteTime", + description: "Time to jump afer exiting a cliff in s", + default: 0.1, + }, + { + type: "number", + name: "jumpBuffer", + description: "Time before hitting the ground counting as a jump in s", + default: 0.1, + }, + + ], +}; diff --git a/packages/nf-jump-controller-2d.component/nanoforge.manifest.json b/packages/nf-jump-controller-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..be280d1 --- /dev/null +++ b/packages/nf-jump-controller-2d.component/nanoforge.manifest.json @@ -0,0 +1,9 @@ +{ + "name": "nf/jump-controller-2d", + "type": "component", + "description": "Allow jump upwards", + "tags": ["base", "mouvement"], + "libDependencies": { + "@nanoforge-dev/input": "^1.0.2" + } +} diff --git a/packages/nf-move-control-2d.system/index.ts b/packages/nf-move-control-2d.system/index.ts index 85f04c6..3b2579b 100644 --- a/packages/nf-move-control-2d.system/index.ts +++ b/packages/nf-move-control-2d.system/index.ts @@ -15,17 +15,27 @@ export function moveControl2D(registry: Registry, ctx: Context) { const rightPressed = input.isKeyPressed(MoveController2D.keyRight); const leftPressed = input.isKeyPressed(MoveController2D.keyLeft); - let velX = 0; - if (leftPressed != rightPressed) { - velX = rightPressed ? MoveController2D.speedRight : -MoveController2D.speedLeft; + if (MoveController2D.keyRight || MoveController2D.keyLeft) { + let velX = 0; + if (leftPressed != rightPressed) { + if (rightPressed) + velX = MoveController2D.speedRight; + if (leftPressed) + velX = -MoveController2D.speedLeft; + } + Velocity2D.x = velX; } - Velocity2D.x = velX; - let velY = 0; - if (upPressed != downPressed) { - velY = upPressed ? -MoveController2D.speedUp : MoveController2D.speedDown; + if (MoveController2D.keyUp || MoveController2D.keyDown) { + let velY = 0; + if (upPressed != downPressed) { + if (upPressed) + velY = -MoveController2D.speedUp; + if (downPressed) + velY = MoveController2D.speedDown; + } + Velocity2D.y = velY; } - Velocity2D.y = velY; }); } // * Required to generate code diff --git a/packages/nf-move-controller-2d.component/index.ts b/packages/nf-move-controller-2d.component/index.ts index f7d6202..4efe148 100644 --- a/packages/nf-move-controller-2d.component/index.ts +++ b/packages/nf-move-controller-2d.component/index.ts @@ -1,5 +1,5 @@ import type { EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; -import { InputEnum } from "@nanoforge-dev/input"; +import { type InputEnum } from "@nanoforge-dev/input"; export class MoveController2D { name = this.constructor.name; @@ -9,11 +9,12 @@ export class MoveController2D { public speedDown: number, public speedLeft: number, public speedRight: number, - public keyUp: InputEnum, - public keyDown: InputEnum, - public keyLeft: InputEnum, - public keyRight: InputEnum, - ) {} + public keyUp?: InputEnum, + public keyDown?: InputEnum, + public keyLeft?: InputEnum, + public keyRight?: InputEnum, + ) { } + } // * Required to generate code @@ -56,34 +57,630 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { { name: "keyUp", type: "string", - enum: InputEnum, + optional: true, + enum: [ + "Escape", + "Digit1", + "Digit2", + "Digit3", + "Digit4", + "Digit5", + "Digit6", + "Digit7", + "Digit8", + "Digit9", + "Digit0", + "Minus", + "Equal", + "Backspace", + "Tab", + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Enter", + "ControlLeft", + "KeyA", + "KeyS", + "KeyD", + "KeyF", + "KeyG", + "KeyH", + "KeyJ", + "KeyK", + "KeyL", + "Semicolon", + "Quote", + "Backquote", + "ShiftLeft", + "Backslash", + "KeyZ", + "KeyX", + "KeyC", + "KeyV", + "KeyB", + "KeyN", + "KeyM", + "Comma", + "Period", + "Slash", + "ShiftRight", + "NumpadMultiply", + "AltLeft", + "Space", + "CapsLock", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "Pause", + "ScrollLock", + "Numpad7", + "Numpad8", + "Numpad9", + "NumpadSubtract", + "Numpad4", + "Numpad5", + "Numpad6", + "NumpadAdd", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad0", + "NumpadDecimal", + "IntlBackslash", + "F11", + "F12", + "NumpadEqual", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "KanaMode", + "Lang2", + "Lang1", + "IntlRo", + "F24", + "Convert", + "NonConvert", + "IntlYen", + "NumpadComma", + "MediaTrackPrevious", + "MediaTrackNext", + "NumpadEnter", + "ControlRight", + "AudioVolumeMute", + "LaunchApp2", + "MediaPlayPause", + "MediaStop", + "BrowserHome", + "NumpadDivide", + "PrintScreen", + "AltRight", + "NumLock", + "Home", + "ArrowUp", + "PageUp", + "ArrowLeft", + "ArrowRight", + "End", + "ArrowDown", + "PageDown", + "Insert", + "Delete", + "MetaLeft", + "MetaRight", + "ContextMenu", + "Power", + "BrowserSearch", + "BrowserFavorites", + "BrowserRefresh", + "BrowserStop", + "BrowserForward", + "BrowserBack", + "LaunchApp1", + "LaunchMail", + "MouseLeft", + "MouseMiddle", + "MouseRight", + "BackButton", + "Forward" + ], description: "Key to press to go up", - example: InputEnum.ArrowUp, - default: InputEnum.ArrowUp, + example: "ArrowUp", + default: "ArrowUp", }, { name: "keyDown", type: "string", - enum: InputEnum, + optional: true, + enum: [ + "Escape", + "Digit1", + "Digit2", + "Digit3", + "Digit4", + "Digit5", + "Digit6", + "Digit7", + "Digit8", + "Digit9", + "Digit0", + "Minus", + "Equal", + "Backspace", + "Tab", + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Enter", + "ControlLeft", + "KeyA", + "KeyS", + "KeyD", + "KeyF", + "KeyG", + "KeyH", + "KeyJ", + "KeyK", + "KeyL", + "Semicolon", + "Quote", + "Backquote", + "ShiftLeft", + "Backslash", + "KeyZ", + "KeyX", + "KeyC", + "KeyV", + "KeyB", + "KeyN", + "KeyM", + "Comma", + "Period", + "Slash", + "ShiftRight", + "NumpadMultiply", + "AltLeft", + "Space", + "CapsLock", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "Pause", + "ScrollLock", + "Numpad7", + "Numpad8", + "Numpad9", + "NumpadSubtract", + "Numpad4", + "Numpad5", + "Numpad6", + "NumpadAdd", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad0", + "NumpadDecimal", + "IntlBackslash", + "F11", + "F12", + "NumpadEqual", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "KanaMode", + "Lang2", + "Lang1", + "IntlRo", + "F24", + "Convert", + "NonConvert", + "IntlYen", + "NumpadComma", + "MediaTrackPrevious", + "MediaTrackNext", + "NumpadEnter", + "ControlRight", + "AudioVolumeMute", + "LaunchApp2", + "MediaPlayPause", + "MediaStop", + "BrowserHome", + "NumpadDivide", + "PrintScreen", + "AltRight", + "NumLock", + "Home", + "ArrowUp", + "PageUp", + "ArrowLeft", + "ArrowRight", + "End", + "ArrowDown", + "PageDown", + "Insert", + "Delete", + "MetaLeft", + "MetaRight", + "ContextMenu", + "Power", + "BrowserSearch", + "BrowserFavorites", + "BrowserRefresh", + "BrowserStop", + "BrowserForward", + "BrowserBack", + "LaunchApp1", + "LaunchMail", + "MouseLeft", + "MouseMiddle", + "MouseRight", + "BackButton", + "Forward" + ], description: "Key to press to go down", - example: InputEnum.ArrowDown, - default: InputEnum.ArrowDown, + example: "ArrowDown", + default: "ArrowDown", }, { name: "keyLeft", type: "string", - enum: InputEnum, + optional: true, + enum: [ + "Escape", + "Digit1", + "Digit2", + "Digit3", + "Digit4", + "Digit5", + "Digit6", + "Digit7", + "Digit8", + "Digit9", + "Digit0", + "Minus", + "Equal", + "Backspace", + "Tab", + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Enter", + "ControlLeft", + "KeyA", + "KeyS", + "KeyD", + "KeyF", + "KeyG", + "KeyH", + "KeyJ", + "KeyK", + "KeyL", + "Semicolon", + "Quote", + "Backquote", + "ShiftLeft", + "Backslash", + "KeyZ", + "KeyX", + "KeyC", + "KeyV", + "KeyB", + "KeyN", + "KeyM", + "Comma", + "Period", + "Slash", + "ShiftRight", + "NumpadMultiply", + "AltLeft", + "Space", + "CapsLock", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "Pause", + "ScrollLock", + "Numpad7", + "Numpad8", + "Numpad9", + "NumpadSubtract", + "Numpad4", + "Numpad5", + "Numpad6", + "NumpadAdd", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad0", + "NumpadDecimal", + "IntlBackslash", + "F11", + "F12", + "NumpadEqual", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "KanaMode", + "Lang2", + "Lang1", + "IntlRo", + "F24", + "Convert", + "NonConvert", + "IntlYen", + "NumpadComma", + "MediaTrackPrevious", + "MediaTrackNext", + "NumpadEnter", + "ControlRight", + "AudioVolumeMute", + "LaunchApp2", + "MediaPlayPause", + "MediaStop", + "BrowserHome", + "NumpadDivide", + "PrintScreen", + "AltRight", + "NumLock", + "Home", + "ArrowUp", + "PageUp", + "ArrowLeft", + "ArrowRight", + "End", + "ArrowDown", + "PageDown", + "Insert", + "Delete", + "MetaLeft", + "MetaRight", + "ContextMenu", + "Power", + "BrowserSearch", + "BrowserFavorites", + "BrowserRefresh", + "BrowserStop", + "BrowserForward", + "BrowserBack", + "LaunchApp1", + "LaunchMail", + "MouseLeft", + "MouseMiddle", + "MouseRight", + "BackButton", + "Forward" + ], description: "Key to press to go left", - example: InputEnum.ArrowLeft, - default: InputEnum.ArrowLeft, + example: "ArrowLeft", + default: "ArrowLeft", }, { name: "keyRight", type: "string", - enum: InputEnum, + optional: true, + enum: [ + "Escape", + "Digit1", + "Digit2", + "Digit3", + "Digit4", + "Digit5", + "Digit6", + "Digit7", + "Digit8", + "Digit9", + "Digit0", + "Minus", + "Equal", + "Backspace", + "Tab", + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Enter", + "ControlLeft", + "KeyA", + "KeyS", + "KeyD", + "KeyF", + "KeyG", + "KeyH", + "KeyJ", + "KeyK", + "KeyL", + "Semicolon", + "Quote", + "Backquote", + "ShiftLeft", + "Backslash", + "KeyZ", + "KeyX", + "KeyC", + "KeyV", + "KeyB", + "KeyN", + "KeyM", + "Comma", + "Period", + "Slash", + "ShiftRight", + "NumpadMultiply", + "AltLeft", + "Space", + "CapsLock", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "Pause", + "ScrollLock", + "Numpad7", + "Numpad8", + "Numpad9", + "NumpadSubtract", + "Numpad4", + "Numpad5", + "Numpad6", + "NumpadAdd", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad0", + "NumpadDecimal", + "IntlBackslash", + "F11", + "F12", + "NumpadEqual", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "KanaMode", + "Lang2", + "Lang1", + "IntlRo", + "F24", + "Convert", + "NonConvert", + "IntlYen", + "NumpadComma", + "MediaTrackPrevious", + "MediaTrackNext", + "NumpadEnter", + "ControlRight", + "AudioVolumeMute", + "LaunchApp2", + "MediaPlayPause", + "MediaStop", + "BrowserHome", + "NumpadDivide", + "PrintScreen", + "AltRight", + "NumLock", + "Home", + "ArrowUp", + "PageUp", + "ArrowLeft", + "ArrowRight", + "End", + "ArrowDown", + "PageDown", + "Insert", + "Delete", + "MetaLeft", + "MetaRight", + "ContextMenu", + "Power", + "BrowserSearch", + "BrowserFavorites", + "BrowserRefresh", + "BrowserStop", + "BrowserForward", + "BrowserBack", + "LaunchApp1", + "LaunchMail", + "MouseLeft", + "MouseMiddle", + "MouseRight", + "BackButton", + "Forward" + ], description: "Key to press to go right", - example: InputEnum.ArrowRight, - default: InputEnum.ArrowRight, + example: "ArrowRight", + default: "ArrowRight", }, ], }; diff --git a/packages/nf-owner.component/index.ts b/packages/nf-owner.component/index.ts new file mode 100644 index 0000000..2e08622 --- /dev/null +++ b/packages/nf-owner.component/index.ts @@ -0,0 +1,21 @@ +import { Entity, type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class Owner { + name = this.constructor.name; + + public owner: Entity + + constructor( + ) { } +} + +// * Required to generate code +export default Owner.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "Owner", + description: "Store the other entity that own this one", + params: [ + ], +}; diff --git a/packages/nf-owner.component/nanoforge.manifest.json b/packages/nf-owner.component/nanoforge.manifest.json new file mode 100644 index 0000000..4300657 --- /dev/null +++ b/packages/nf-owner.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/owner", + "type": "component", + "description": "Store the other entity that own this one", + "tags": ["base", "health"] +} diff --git a/packages/nf-position-2d.component/index.ts b/packages/nf-position-2d.component/index.ts index 02b0d85..2e89f1b 100644 --- a/packages/nf-position-2d.component/index.ts +++ b/packages/nf-position-2d.component/index.ts @@ -23,7 +23,6 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { description: "Horizontal position in pixel", example: 4.2, default: 0, - optional: true, }, { type: "number", @@ -31,7 +30,6 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { description: "Vertical position in pixel", example: 67, default: 0, - optional: true, }, ], }; diff --git a/packages/nf-rigid-body-2d.component/index.ts b/packages/nf-rigid-body-2d.component/index.ts new file mode 100644 index 0000000..57064d2 --- /dev/null +++ b/packages/nf-rigid-body-2d.component/index.ts @@ -0,0 +1,26 @@ +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class RigidBody2D { + name = this.constructor.name; + + constructor( + public gravityStrength: number, + ) { } +} + +// * Required to generate code +export default RigidBody2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "RigidBody2D", + description: "Rigid body of movable object, afected by gravity, blocked by static body", + params: [ + { + type: "number", + name: "gravityStrength", + description: "Strength of the gravity (acting as down acceleration) in px/s²", + default: 0, + }, + ], +}; diff --git a/packages/nf-rigid-body-2d.component/nanoforge.manifest.json b/packages/nf-rigid-body-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..8cd7d33 --- /dev/null +++ b/packages/nf-rigid-body-2d.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/rigid-body-2d", + "type": "component", + "description": "Rigid body of movable object, afected by gravity, blocked by static body", + "tags": ["base", "mouvement"] +} diff --git a/packages/nf-shoot-control-2d.system/index.ts b/packages/nf-shoot-control-2d.system/index.ts new file mode 100644 index 0000000..ccbfa5e --- /dev/null +++ b/packages/nf-shoot-control-2d.system/index.ts @@ -0,0 +1,76 @@ +import { type Context } from "@nanoforge-dev/common"; +import { type EditorSystemManifest, type Registry } from "@nanoforge-dev/ecs-lib"; +import { type InputLibrary } from "@nanoforge-dev/input"; +import { Graphics2DLibrary, Vector2d } from "@nanoforge-dev/graphics-2d"; + +import { ShootController2D } from "../components/nf-shoot-controller-2d.component"; +import { Position2D } from "../components/nf-position-2d.component"; +import { Velocity2D } from "../components/nf-velocity-2d.component"; +import { HitboxCircle2D } from "../components/nf-hitbox-circle-2d.component"; +import { DrawableCircle2D } from "../components/nf-drawable-circle-2d.component"; +import { DamageDealer } from "../components/nf-damage-dealer.component"; +import { HitboxRectangle2D } from "../components/nf-hitbox-rectangle-2d.component"; +import { DestroyOnHit2D } from "../components/nf-destroy-on-hit-2d.component"; +import { Owner } from "../components/nf-owner.component"; + +function createBullet(registry: Registry, id: number, p: Vector2d, m: Vector2d, s: ShootController2D) { + const b = registry.spawnEntity() + + if (!m?.x || !m?.y) return + const dx = m.x - p.x; + const dy = m.y - p.y; + + const length = Math.sqrt(dx * dx + dy * dy); + + const dirX = dx / length; + const dirY = dy / length; + + registry.addComponent(b, new Position2D(p.x, p.y)); + registry.addComponent(b, new Velocity2D(dirX * s.bulletSpeed, dirY * s.bulletSpeed)); + registry.addComponent(b, new HitboxCircle2D(s.bulletSize)) + registry.addComponent(b, new DrawableCircle2D(s.bulletSize, true, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, undefined, "grey")) + registry.addComponent(b, new DamageDealer(s.bulletDamage)) + registry.addComponent(b, new DestroyOnHit2D(false, true)) + const owner = new Owner(); + owner.owner = registry.entityFromIndex(id) + registry.addComponent(b, owner) +} + +export function shootControl2D(registry: Registry, ctx: Context) { + const entities = registry.getIndexedZipper([ShootController2D, Position2D, HitboxRectangle2D]); + const input = ctx.libs.getInput(); + const graphic2d = ctx.libs.getGraphics(); + + entities.forEach(({ id, ShootController2D, Position2D, HitboxRectangle2D }) => { + ShootController2D.reloadTimer -= ctx.app.delta / 1000; + + const keyPressed = input.isKeyPressed(ShootController2D.key); + + if (keyPressed && !ShootController2D.keyPressedLastFrame && ShootController2D.reloadTimer <= 0) { + createBullet(registry, id, { + x: Position2D.x + HitboxRectangle2D.width / 2, + y: Position2D.y + HitboxRectangle2D.height / 2 + }, graphic2d.stage.getPointerPosition(), ShootController2D) + ShootController2D.reloadTimer = ShootController2D.reloadTime; + } + }); +} +// * Required to generate code +export default shootControl2D.name; + +// * Required for the editor to display the system and generate code +export const EDITOR_SYSTEM_MANIFEST: EditorSystemManifest = { + name: "shootControl2D", + description: "Control entities that can shoot bullets at mouse position", + dependencies: [ + "ShootController2D", + "Position2D", + "Velocity2D", + "HitboxRectangle2D", + "HitboxCircle2D", + "DrawableCircle2D", + "DamageDealer", + "DestroyOnHit2D", + "Owner", + ], +}; diff --git a/packages/nf-shoot-control-2d.system/nanoforge.manifest.json b/packages/nf-shoot-control-2d.system/nanoforge.manifest.json new file mode 100644 index 0000000..fe716d6 --- /dev/null +++ b/packages/nf-shoot-control-2d.system/nanoforge.manifest.json @@ -0,0 +1,20 @@ +{ + "name": "nf/shoot-control-2d", + "type": "system", + "description": "Control entities that can shoot bullets at mouse position", + "tags": ["base", "mouvement"], + "dependencies": [ + "nf/shoot-controller-2d", + "nf/position-2d", + "nf/velocity-2d", + "nf/hitbox-rectangle-2d", + "nf/hitbox-circle-2d", + "nf/drawable-circle-2d", + "nf/damage-dealer-2d", + "nf/destroy-on-hit-2d", + "nf/owner" + ], + "libDependencies": { + "@nanoforge-dev/input": "^1.0.2" + } +} diff --git a/packages/nf-shoot-controller-2d.component/index.ts b/packages/nf-shoot-controller-2d.component/index.ts new file mode 100644 index 0000000..946266b --- /dev/null +++ b/packages/nf-shoot-controller-2d.component/index.ts @@ -0,0 +1,206 @@ +import type { EditorComponentManifest, Entity } from "@nanoforge-dev/ecs-lib"; +import { type InputEnum } from "@nanoforge-dev/input"; + +export class ShootController2D { + name = this.constructor.name; + public reloadTimer: number = 0; + + constructor( + public bulletDamage: number, + public key: InputEnum, + public reloadTime: number, + public bulletSpeed: number, + public bulletSize: number, + ) { + } +} + +// * Required to generate code +export default ShootController2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "ShootController2D", + description: "Allow shoot at mouse position", + params: [ + { + type: "number", + name: "bulletDamage", + description: "Damage dealt by the bullet", + default: 10, + }, + { + name: "key", + type: "string", + enum: [ + "Escape", + "Digit1", + "Digit2", + "Digit3", + "Digit4", + "Digit5", + "Digit6", + "Digit7", + "Digit8", + "Digit9", + "Digit0", + "Minus", + "Equal", + "Backspace", + "Tab", + "KeyQ", + "KeyW", + "KeyE", + "KeyR", + "KeyT", + "KeyY", + "KeyU", + "KeyI", + "KeyO", + "KeyP", + "BracketLeft", + "BracketRight", + "Enter", + "ControlLeft", + "KeyA", + "KeyS", + "KeyD", + "KeyF", + "KeyG", + "KeyH", + "KeyJ", + "KeyK", + "KeyL", + "Semicolon", + "Quote", + "Backquote", + "ShiftLeft", + "Backslash", + "KeyZ", + "KeyX", + "KeyC", + "KeyV", + "KeyB", + "KeyN", + "KeyM", + "Comma", + "Period", + "Slash", + "ShiftRight", + "NumpadMultiply", + "AltLeft", + "Space", + "CapsLock", + "F1", + "F2", + "F3", + "F4", + "F5", + "F6", + "F7", + "F8", + "F9", + "F10", + "Pause", + "ScrollLock", + "Numpad7", + "Numpad8", + "Numpad9", + "NumpadSubtract", + "Numpad4", + "Numpad5", + "Numpad6", + "NumpadAdd", + "Numpad1", + "Numpad2", + "Numpad3", + "Numpad0", + "NumpadDecimal", + "IntlBackslash", + "F11", + "F12", + "NumpadEqual", + "F13", + "F14", + "F15", + "F16", + "F17", + "F18", + "F19", + "F20", + "F21", + "F22", + "F23", + "KanaMode", + "Lang2", + "Lang1", + "IntlRo", + "F24", + "Convert", + "NonConvert", + "IntlYen", + "NumpadComma", + "MediaTrackPrevious", + "MediaTrackNext", + "NumpadEnter", + "ControlRight", + "AudioVolumeMute", + "LaunchApp2", + "MediaPlayPause", + "MediaStop", + "BrowserHome", + "NumpadDivide", + "PrintScreen", + "AltRight", + "NumLock", + "Home", + "ArrowUp", + "PageUp", + "ArrowLeft", + "ArrowRight", + "End", + "ArrowDown", + "PageDown", + "Insert", + "Delete", + "MetaLeft", + "MetaRight", + "ContextMenu", + "Power", + "BrowserSearch", + "BrowserFavorites", + "BrowserRefresh", + "BrowserStop", + "BrowserForward", + "BrowserBack", + "LaunchApp1", + "LaunchMail", + "MouseLeft", + "MouseMiddle", + "MouseRight", + "BackButton", + "Forward" + ], + description: "Key to press shoot", + default: "MouseLeft", + }, + { + type: "number", + name: "reloadTime", + description: "Time between 2 bullets in s", + default: 0.3, + }, + { + type: "number", + name: "bulletSpeed", + description: "Speed of the bullet in px/s", + default: 600, + }, + { + type: "number", + name: "bulletSize", + description: "Radius of the bullet in px", + default: 40, + }, + ], +}; diff --git a/packages/nf-shoot-controller-2d.component/nanoforge.manifest.json b/packages/nf-shoot-controller-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..92238d5 --- /dev/null +++ b/packages/nf-shoot-controller-2d.component/nanoforge.manifest.json @@ -0,0 +1,9 @@ +{ + "name": "nf/shoot-controller-2d", + "type": "component", + "description": "Allow shoot at mouse position", + "tags": ["base", "mouvement"], + "libDependencies": { + "@nanoforge-dev/input": "^1.0.2" + } +} diff --git a/packages/nf-static-body-2d.component/index.ts b/packages/nf-static-body-2d.component/index.ts new file mode 100644 index 0000000..f112588 --- /dev/null +++ b/packages/nf-static-body-2d.component/index.ts @@ -0,0 +1,32 @@ +import { type EditorComponentManifest } from "@nanoforge-dev/ecs-lib"; + +export class StaticBody2D { + name = this.constructor.name; + + constructor( + public oneWay?: string, + ) { } +} + +// * Required to generate code +export default StaticBody2D.name; + +// * Required for the editor to display the component and generate code +export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { + name: "StaticBody2D", + description: "Static body of immovable object (walls, grounds...)", + params: [ + { + name: "oneWay", + type: "string", + enum: [ + "up", + "down", + "left", + "right" + ], + description: "Only traversable in one direction (platform, doors...)", + optional: true + }, + ], +}; diff --git a/packages/nf-static-body-2d.component/nanoforge.manifest.json b/packages/nf-static-body-2d.component/nanoforge.manifest.json new file mode 100644 index 0000000..f833a75 --- /dev/null +++ b/packages/nf-static-body-2d.component/nanoforge.manifest.json @@ -0,0 +1,6 @@ +{ + "name": "nf/static-body-2d", + "type": "component", + "description": "Static body of immovable object (walls, grounds...)", + "tags": ["base", "mouvement"] +} diff --git a/packages/nf-velocity-2d.component/index.ts b/packages/nf-velocity-2d.component/index.ts index 54c54c4..a567f24 100644 --- a/packages/nf-velocity-2d.component/index.ts +++ b/packages/nf-velocity-2d.component/index.ts @@ -6,7 +6,7 @@ export class Velocity2D { constructor( public x: number, public y: number, - ) {} + ) { } } // * Required to generate code @@ -23,7 +23,6 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { description: "Horizontal velocity in px/s", example: 4.2, default: 0, - optional: true, }, { type: "number", @@ -31,7 +30,6 @@ export const EDITOR_COMPONENT_MANIFEST: EditorComponentManifest = { description: "Vertical velocity in px/s", example: 67, default: 0, - optional: true, }, ], };