Skip to content

Commit

Permalink
Matter (#500)
Browse files Browse the repository at this point in the history
* minigame manager

* vector force

* minigame impl

* refactor

* remove itemsProposition

* tweak

* template
  • Loading branch information
keldaan-ag authored Mar 25, 2023
1 parent de71712 commit 79ba263
Show file tree
Hide file tree
Showing 28 changed files with 957 additions and 325 deletions.
233 changes: 233 additions & 0 deletions app/core/matter/mini-game.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
import { PokemonAvatar } from "../../models/colyseus-models/pokemon-avatar"
import { FloatingItem } from "../../models/colyseus-models/floating-item"
import { Schema, MapSchema, type } from "@colyseus/schema"
import {
Bodies,
Composite,
Engine,
Body,
Vector,
Events,
Constraint
} from "matter-js"
import Player from "../../models/colyseus-models/player"
import { getOrientation } from "../../public/src/pages/utils/utils"
import { PokemonActionState } from "../../types/enum/Game"
import { BasicItems, Item } from "../../types/enum/Item"
import { pickRandomIn } from "../../utils/random"

export class MiniGame {
avatars: MapSchema<PokemonAvatar> | undefined
items: MapSchema<FloatingItem> | undefined
bodies: Map<string, Body>
engine: Engine
centerX: number = 325
centerY: number = 250

constructor() {
this.engine = Engine.create({ gravity: { x: 0, y: 0 } })
this.bodies = new Map<string, Body>()
Composite.add(
this.engine.world,
Bodies.rectangle(0, -70, 2000, 40, { isStatic: true, restitution: 1 })
)
Composite.add(
this.engine.world,
Bodies.rectangle(-70, 0, 40, 2000, { isStatic: true, restitution: 1 })
)
Composite.add(
this.engine.world,
Bodies.rectangle(740, 0, 40, 2000, { isStatic: true, restitution: 1 })
)
Composite.add(
this.engine.world,
Bodies.rectangle(0, 610, 2000, 40, { isStatic: true, restitution: 1 })
)
Events.on(this.engine, "beforeUpdate", (event) => {
this.items?.forEach((item) => {
if (item.avatarId === "") {
const itemBody = this.bodies.get(item.id)
if (itemBody) {
const x =
this.centerX +
Math.cos(
this.engine.timing.timestamp * 0.0005 +
(Math.PI * item.index) / 4.5
) *
100
const y =
this.centerY +
Math.sin(
this.engine.timing.timestamp * 0.0005 +
(Math.PI * item.index) / 4.5
) *
90
Body.setPosition(itemBody, { x: x, y: y })
}
}
})
})

Events.on(this.engine, "collisionStart", (event) => {
const pairs = event.pairs
if (
(this.items?.has(pairs[0].bodyA.label) ||
this.items?.has(pairs[0].bodyB.label)) &&
(this.avatars?.has(pairs[0].bodyA.label) ||
this.avatars?.has(pairs[0].bodyB.label))
) {
let avatar: PokemonAvatar | undefined,
avatarBody: Body | undefined,
item: FloatingItem | undefined,
itemBody: Body | undefined

if (this.avatars?.has(pairs[0].bodyA.label)) {
avatar = this.avatars.get(pairs[0].bodyA.label)
avatarBody = pairs[0].bodyA
item = this.items.get(pairs[0].bodyB.label)
itemBody = pairs[0].bodyB
} else {
avatar = this.avatars.get(pairs[0].bodyB.label)
avatarBody = pairs[0].bodyB
item = this.items.get(pairs[0].bodyA.label)
itemBody = pairs[0].bodyA
}

if (
itemBody &&
avatarBody &&
avatar &&
item &&
avatar.itemId === "" &&
item.avatarId === ""
) {
const constraint = Constraint.create({
bodyA: avatarBody,
bodyB: itemBody
})
Composite.add(this.engine.world, constraint)
avatar.itemId = item.id
item.avatarId = avatar.id
}
}
})
}

create(avatars: MapSchema<PokemonAvatar>, items: MapSchema<FloatingItem>) {
this.avatars = avatars
this.items = items
}

initialize(players: MapSchema<Player>) {
let i = 0
let alivePlayers = new Array<Player>()
players.forEach((p) => {
if (p.alive) {
alivePlayers.push(p)
}
})
alivePlayers.forEach((player) => {
const x =
this.centerX + Math.cos((2 * Math.PI * i) / alivePlayers.length) * 300
const y =
this.centerY + Math.sin((2 * Math.PI * i) / alivePlayers.length) * 250
const avatar = new PokemonAvatar(
player.id,
player.avatar,
x,
y,
11000 - player.rank * 1000
)

this.avatars!.set(avatar.id, avatar)
const body = Bodies.circle(x, y, 25)
body.label = avatar.id
this.bodies.set(avatar.id, body)
Composite.add(this.engine.world, body)
i++
})
for (let j = 0; j < 9; j++) {
const x = this.centerX + Math.cos((Math.PI * j) / 4.5) * 100
const y = this.centerY + Math.sin((Math.PI * j) / 4.5) * 90
const name = pickRandomIn(BasicItems)
const floatingItem = new FloatingItem(name, x, y, j)
this.items?.set(floatingItem.id, floatingItem)
const body = Bodies.circle(x, y, 15)
body.label = floatingItem.id
this.bodies.set(floatingItem.id, body)
Composite.add(this.engine.world, body)
}
}

update(dt: number) {
Engine.update(this.engine, dt)
this.avatars?.forEach((a) => {
if (a.timer > 0) {
a.timer = a.timer - dt
}
})
this.bodies.forEach((body, key) => {
const avatar = this.avatars?.get(key)
if (avatar) {
if (
avatar.action === PokemonActionState.IDLE &&
(Math.abs(body.velocity.x) > 0.1 || Math.abs(body.velocity.y) > 0.1)
) {
avatar.action = PokemonActionState.WALK
} else if (
avatar.action === PokemonActionState.WALK &&
Math.abs(body.velocity.x) < 0.1 &&
Math.abs(body.velocity.y) < 0.1
) {
avatar.action = PokemonActionState.IDLE
}
avatar.x = body.position.x
avatar.y = body.position.y
}
const item = this.items?.get(key)
if (item) {
item.x = body.position.x
item.y = body.position.y
}
})
}

applyVector(id: string, x: number, y: number) {
const avatar = this.avatars?.get(id)
if (avatar && avatar.timer <= 0) {
const norm = Math.sqrt(x ** 2 + y ** 2)
const normX = x / norm
const normY = y / norm
const body = this.bodies.get(id)

avatar.orientation = getOrientation(0, 0, x, y)
if (body) {
Body.applyForce(
body,
body.position,
Vector.create(normX / 700, -normY / 700)
)
}
}
}

stop(players: MapSchema<Player>) {
this.bodies.forEach((body, key) => {
Composite.remove(this.engine.world, body)
this.bodies.delete(key)
})
this.avatars!.forEach((avatar) => {
if (avatar.itemId !== "") {
const item = this.items?.get(avatar.itemId)
const player = players.get(avatar.id)
if (item && player) {
player.items.add(item.name)
}
}
this.avatars!.delete(avatar.id)
})
this.items!.forEach((item) => {
this.items!.delete(item.id)
})
}
}
21 changes: 21 additions & 0 deletions app/models/colyseus-models/floating-item.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { Schema, type } from "@colyseus/schema"
import { IFloatingItem } from "../../types"
import { nanoid } from "nanoid"
import { Item } from "../../types/enum/Item"

export class FloatingItem extends Schema implements IFloatingItem {
@type("string") id: string = nanoid()
@type("string") name: Item = Item.LEFTOVERS
@type("number") x: number
@type("number") y: number
avatarId: string = ""
index: number

constructor(name: Item, x: number, y: number, index: number) {
super()
this.name = name
this.x = x
this.y = y
this.index = index
}
}
1 change: 0 additions & 1 deletion app/models/colyseus-models/player.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export default class Player extends Schema implements IPlayer {
@type(Simulation) simulation
@type(ExperienceManager) experienceManager = new ExperienceManager()
@type({ map: "uint8" }) synergies = new Synergies()
@type(["string"]) itemsProposition = new ArraySchema<Item>()
@type("uint8") money = process.env.MODE == "dev" ? 400 : 6
@type("uint8") life = 100
@type("boolean") shopLocked: boolean = false
Expand Down
36 changes: 36 additions & 0 deletions app/models/colyseus-models/pokemon-avatar.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { Schema, type } from "@colyseus/schema"
import { Constraint } from "matter-js"
import { getInformations } from "../../public/src/utils"
import { IPokemonAvatar } from "../../types"
import { Orientation, PokemonActionState } from "../../types/enum/Game"
import { Pkm, PkmIndex } from "../../types/enum/Pokemon"

export class PokemonAvatar extends Schema implements IPokemonAvatar {
@type("string") id: string
@type("string") name: Pkm = Pkm.RATTATA
@type("boolean") shiny: boolean
@type("number") x: number
@type("number") y: number
@type("string") action: PokemonActionState = PokemonActionState.IDLE
@type("string") orientation: Orientation = Orientation.DOWNLEFT
itemId: string = ""
timer: number
constraint: Constraint | undefined

constructor(id: string, avatar: string, x: number, y: number, timer: number) {
super()
this.id = id
this.x = x
this.y = y
this.timer = timer
const informations = getInformations(avatar)
Object.keys(PkmIndex).forEach((pkm_) => {
const pkm = pkm_ as Pkm
const index = PkmIndex[pkm]
if (index === informations.index) {
this.name = pkm
}
})
this.shiny = informations.shiny
}
}
31 changes: 22 additions & 9 deletions app/models/shop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -292,38 +292,51 @@ export default class Shop {

assignMythicalPropositions(player: Player, list: Pkm[]) {
const mythicals = [...list]
const synergies = Array.from(player.synergies).filter(([synergy, value]) => value > 0).map(([synergy, value]) => synergy)
const top2Synergies = Array.from(player.synergies).sort(([s1, v1], [s2, v2]) => v2 - v1).slice(0,2).map(([synergy, value]) => synergy)
const synergies = Array.from(player.synergies)
.filter(([synergy, value]) => value > 0)
.map(([synergy, value]) => synergy)
const top2Synergies = Array.from(player.synergies)
.sort(([s1, v1], [s2, v2]) => v2 - v1)
.slice(0, 2)
.map(([synergy, value]) => synergy)

const mythicalsTopSynergy = mythicals.filter(m => PokemonFactory.createPokemonFromName(m).types.some(t => top2Synergies.includes(t)))
const mythicalsCommonSynergy = mythicals.filter(m => PokemonFactory.createPokemonFromName(m).types.some(t => synergies.includes(t)))
const mythicalsTopSynergy = mythicals.filter((m) =>
PokemonFactory.createPokemonFromName(m).types.some((t) =>
top2Synergies.includes(t)
)
)
const mythicalsCommonSynergy = mythicals.filter((m) =>
PokemonFactory.createPokemonFromName(m).types.some((t) =>
synergies.includes(t)
)
)

const shop: Pkm[] = []
if(mythicalsTopSynergy.length > 0){
if (mythicalsTopSynergy.length > 0) {
const pkm = pickRandomIn(mythicalsTopSynergy)
removeInArray(mythicals, pkm)
removeInArray(mythicalsTopSynergy, pkm)
removeInArray(mythicalsCommonSynergy, pkm)
shop.push(pkm)
}

for(let i=0; i<2; i++){
if(mythicalsCommonSynergy.length > 0){
for (let i = 0; i < 2; i++) {
if (mythicalsCommonSynergy.length > 0) {
const pkm = pickRandomIn(mythicalsCommonSynergy)
removeInArray(mythicals, pkm)
removeInArray(mythicalsCommonSynergy, pkm)
shop.push(pkm)
}
}

while(shop.length < 6){
while (shop.length < 6) {
const pkm = pickRandomIn(mythicals)
removeInArray(mythicals, pkm)
shop.push(pkm)
}

shuffleArray(shop)
shop.forEach(pkm => player.pokemonsProposition.push(pkm))
shop.forEach((pkm) => player.pokemonsProposition.push(pkm))
}

getRandomPokemonFromPool(pool: Map<Pkm, number>, finals: Array<Pkm>): Pkm {
Expand Down
Binary file modified app/public/dist/client/assets/ui/template.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified app/public/dist/client/assets/ui/template.xcf
Binary file not shown.
2 changes: 1 addition & 1 deletion app/public/dist/client/sw.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/* Change this cache name every time you want to force players
to invalidate their cache and download all assets again */

const CACHE_NAME = 'CACHE v2.9.1';
const CACHE_NAME = 'CACHE v3.0.0';

// Cache-first strategy
const cacheFirst = (event) => {
Expand Down
12 changes: 12 additions & 0 deletions app/public/src/game/animation-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1519,6 +1519,18 @@ export default class AnimationManager {
repeat: -1
})

this.game.anims.create({
key: "WATER/cell",
frames: this.game.anims.generateFrameNames("attacks", {
start: 0,
end: 6,
zeroPad: 3,
prefix: "WATER/cell/"
}),
duration: 200,
repeat: 0
})

this.game.anims.create({
key: Ability.FAKE_TEARS,
frames: this.game.anims.generateFrameNames(Ability.FAKE_TEARS, {
Expand Down
Loading

0 comments on commit 79ba263

Please sign in to comment.