import { Logger } from "winston" ; import { v4 as uuid } from "uuid" ; import { shuffle } from "lodash" ; import { CreatureDefinition , Card , PieceModel , PIECES_TO_EVOLVE } from "@creature-chess/models" ; import { getAllDefinitions , getDefinitionById } from "../definitions" ; const CARD_COST_CHANCES = [ [ 100 , 70 , 60 , 50 , 40 , 33 , 30 , 24 , 22 , 19 ] , [ 0 , 30 , 35 , 35 , 35 , 30 , 30 , 30 , 30 , 25 ] , [ 0 , 0 , 5 , 15 , 23 , 30 , 30 , 30 , 25 , 25 ] , [ 0 , 0 , 0 , 2 , 5 , 9 , 12 , 16 , 20 , 25 ] , [ 0 , 0 , 0 , 0 , 1 , 3 , 5 , 7 , 10 , 14 ] ] ; const CARD_LEVEL_QUANTITIES = [ 45 , 30 , 25 , 15 , 10 ] ; const canTakeCardAtCost = ( level , cost ) : boolean => { const chance = CARD_COST_CHANCES [ cost - 1 ] [ level - 1 ] ; if ( ! chance ) { return false ; } const roll = Math . floor ( Math . random ( ) * 100 ) ; return roll <= chance ; } ; export class CardDeck { public deck : Card [ ] [ ] ; public constructor ( private logger ) { this . deck = [ [ ] , [ ] , [ ] , [ ] , [ ] ] ; getAllDefinitions ( ) . filter ( d => d . cost ) . forEach ( d => { for ( let count = 0 ; count < CARD_LEVEL_QUANTITIES [ d . cost - 1 ] ; count ++ ) { this . addDefinition ( d ) ; } } ) ; this . shuffle ( ) ; } public reroll ( input , count , level , excludeCards = [ ] ) { this . addCards ( input ) ; this . shuffle ( ) ; return this . take ( count , level , excludeCards ) ; } public addCards ( cards ) { const cardsToAdd = cards . filter ( card => card !== null ) ; for ( const card of cardsToAdd ) { this . getDeckForCost ( card . cost ) . push ( card ) ; } this . shuffle ( ) ; } public addPiece ( piece ) { const definition = getDefinitionById ( piece . definitionId ) ; if ( ! definition ) { return ; } const cardCount = ( piece . stage + 1 ) * PIECES_TO_EVOLVE ; for ( let i = 0 ; i < cardCount ; i ++ ) { this . addDefinition ( definition ) ; } this . shuffle ( ) ; } public addPieces ( pieces ) { for ( const piece of pieces ) { this . addPiece ( piece ) ; } } public shuffle ( ) { for ( let i = 0 ; i < this . deck . length ; i ++ ) { this . deck [ i ] = shuffle ( this . deck [ i ] ) ; } } private getDeckForCost ( cost ) { return this . deck [ cost - 1 ] ; } private take ( count , level , excludeCards = [ ] ) { const output = [ ] ; for ( let i = 0 ; i < count ; i ++ ) { const takenCard = this . takeCard ( level , excludeCards ) ; if ( ! takenCard ) { continue ; } output . push ( takenCard ) ; } return output ; } private takeCard ( level , excludeDefinitions ) { for ( let cost = CARD_COST_CHANCES . length ; cost >= 1 ; cost -- ) { const roll = canTakeCardAtCost ( level , cost ) ; if ( ! roll ) { continue ; } for ( let i = 0 ; i < 3 ; i ++ ) { const card = this . getDeckForCost ( cost ) . pop ( ) ; if ( card ) { if ( ! excludeDefinitions . includes ( card . definitionId ) ) { return card ; } this . addCards ( [ card ] ) ; } } } this . logger . warn ( "Falling back for second pass to find card" ) ; for ( let cost = 1 ; cost <= CARD_COST_CHANCES . length ; cost ++ ) { for ( let i = 0 ; i < 3 ; i ++ ) { const card = this . getDeckForCost ( cost ) . pop ( ) ; if ( card ) { if ( ! excludeDefinitions . includes ( card . definitionId ) ) { return card ; } this . addCards ( [ card ] ) ; } } } this . logger . error ( "No card found at all" ) ; return null ; } private addDefinition ( definition ) { const card = { id : uuid ( ) , definitionId : definition . id , cost : definition . cost , name : definition . name , type : definition . type , class : definition . class } ; this . getDeckForCost ( definition . cost ) . push ( card ) ; } }