[add] first
This commit is contained in:
152
assets/scripts/logic/level/level-events-card.ts
Normal file
152
assets/scripts/logic/level/level-events-card.ts
Normal file
@@ -0,0 +1,152 @@
|
||||
/*
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://www.cocos.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { _decorator, Component, Node, randomRange, random, randomRangeInt } from 'cc';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { DataLevelInst, DataUpgradeCardInst } from '../data/data-core';
|
||||
import { Local } from '../../core/localization/local';
|
||||
import { Level } from './level';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('LevelEventsCard')
|
||||
export class LevelEventsCard extends Component {
|
||||
|
||||
_interval:number = 0.1;
|
||||
|
||||
probability:any;
|
||||
counter = 0;
|
||||
groupCounter:Array<number> | undefined;
|
||||
|
||||
currentCards: Array<{ name: string; info: any; }> = new Array(3);
|
||||
nextCounter = 2;
|
||||
counterCard = 0;
|
||||
|
||||
start() {
|
||||
this.probability = DataLevelInst._data.probability_drop_card;
|
||||
this.groupCounter = new Array(DataLevelInst._data.cards.length);
|
||||
this._interval = randomRange(this.probability.interval[0], this.probability.interval[1]);
|
||||
|
||||
this.nextCounter = DataUpgradeCardInst._data.next_show_card_param_a;
|
||||
|
||||
Msg.on('msg_kill_enemy', this.checkNextEvent.bind(this));
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
Msg.off('msg_kill_enemy', this.checkNextEvent.bind(this));
|
||||
}
|
||||
|
||||
nextEvent() {
|
||||
|
||||
this.counterCard++;
|
||||
|
||||
this.nextCounter += DataUpgradeCardInst._data.next_show_card_param_a * DataUpgradeCardInst._data.next_show_card_param_b;
|
||||
|
||||
const odds = random();
|
||||
const weights = this.probability.weights;
|
||||
let excludeGroupIndex = -1;
|
||||
|
||||
for(let iWeight = 0; iWeight < weights.length; iWeight++) {
|
||||
if (odds <= weights[iWeight]) {
|
||||
excludeGroupIndex = iWeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (excludeGroupIndex === -1) {
|
||||
throw new Error(`Error calculate weight level events card. value:${odds}`);
|
||||
}
|
||||
|
||||
const currentMax = this.groupCounter![excludeGroupIndex];
|
||||
const weightMax = this.probability.weights_max;
|
||||
|
||||
if (currentMax >= weightMax[excludeGroupIndex]) {
|
||||
this._interval = this.probability.interval_weight_max;
|
||||
return;
|
||||
}
|
||||
|
||||
// Exclude 3, This is temp.
|
||||
const excludeIndex = 3;//this.probability.weights_group[excludeGroupIndex];
|
||||
|
||||
// Get upgrade card list.
|
||||
const cards = DataLevelInst._data.cards;
|
||||
for(let i = 0; i < cards.length; i++) {
|
||||
if(i === excludeIndex) continue;
|
||||
this.currentCards[i] = {
|
||||
name:cards[i],
|
||||
info:this.calculateCardInfo(cards[i])
|
||||
};
|
||||
}
|
||||
|
||||
Level.Instance.currentCards = this.currentCards;
|
||||
|
||||
console.log('Current cards:', this.currentCards);
|
||||
|
||||
Msg.emit('push', 'upgrade_cards');
|
||||
|
||||
this.counter++;
|
||||
this.groupCounter![excludeGroupIndex]++;
|
||||
|
||||
}
|
||||
|
||||
calculateCardInfo(name:string) {
|
||||
|
||||
const upgradeCards = DataUpgradeCardInst._data;
|
||||
const selectCardData = upgradeCards[name];
|
||||
const randomCardIndex = randomRangeInt(0, selectCardData.length);
|
||||
const randomCardData = selectCardData[randomCardIndex];
|
||||
const valueCount = randomCardData.values.length;
|
||||
|
||||
let values = new Array(valueCount);
|
||||
|
||||
let describe = Local.Instance.get(randomCardData.describe);
|
||||
|
||||
for(let i = 0; i < valueCount; i++) {
|
||||
const tempData = randomCardData.values[i];
|
||||
const tempValue = this.calculateRange(tempData.isFloat, tempData.range);
|
||||
const showValue = tempData.isFloat ? `${tempValue * 100} %` : `${tempValue}`;
|
||||
describe = describe.replace(`##${i}##`, showValue);
|
||||
values[i] = {
|
||||
"name":tempData.name,
|
||||
"isFloat":tempData.isFloat,
|
||||
"value": tempValue
|
||||
}
|
||||
}
|
||||
|
||||
return { describe, values }
|
||||
|
||||
}
|
||||
|
||||
calculateRange(isFloat:boolean, range:number[]):number {
|
||||
if(range.length !== 2) return 0;
|
||||
let value = isFloat? randomRange(range[0], range[1]) : randomRangeInt(range[0], range[1]);
|
||||
if(isFloat) value = Number(value.toFixed(2));
|
||||
return value;
|
||||
}
|
||||
|
||||
checkNextEvent(counter:number) {
|
||||
if (counter > this.nextCounter) {
|
||||
this.nextEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
9
assets/scripts/logic/level/level-events-card.ts.meta
Normal file
9
assets/scripts/logic/level/level-events-card.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "c1692bc8-6ad8-499a-ae2a-16f9b994d6c9",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
184
assets/scripts/logic/level/level-events-enemy.ts
Normal file
184
assets/scripts/logic/level/level-events-enemy.ts
Normal file
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://www.cocos.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { _decorator, Component, Node, randomRange, random } from 'cc';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { Level } from './level';
|
||||
import { DataLevelInst } from '../data/data-core';
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('LevelEventsEnemy')
|
||||
export class LevelEventsEnemy extends Component {
|
||||
|
||||
// Used to record the interval at which time occurs.
|
||||
_interval: number = 0.1;
|
||||
|
||||
// The probability data of enemy drops.
|
||||
probability: any;
|
||||
|
||||
// Used for the total number of enemies already generated.
|
||||
counter = 0;
|
||||
|
||||
// Used to count the total number of each grouping.
|
||||
groupCounter: Array<number> | undefined;
|
||||
|
||||
// Total number of deaths.
|
||||
killCounter = 0;
|
||||
|
||||
start () {
|
||||
|
||||
// Get probability.
|
||||
this.probability = DataLevelInst._data.probability_drop_enemy;
|
||||
|
||||
if ((globalThis as any).HrefSetting) {
|
||||
this.probability.max = (globalThis as any).HrefSetting.maxEnemies
|
||||
}
|
||||
|
||||
// Initialize group statistics based on the number of groups.
|
||||
this.groupCounter = new Array(DataLevelInst._data.enemies.length);
|
||||
|
||||
for (let i = 0; i < DataLevelInst._data.enemies.length; i++) {
|
||||
this.groupCounter[i] = 0;
|
||||
}
|
||||
|
||||
// Get the time of the next decision cycle.
|
||||
this._interval = randomRange(this.probability.interval[0], this.probability.interval[1]);
|
||||
|
||||
// Register enemy event logic.
|
||||
Msg.on('msg_remove_enemy', this.remove.bind(this));
|
||||
}
|
||||
|
||||
onDestroy () {
|
||||
|
||||
// Register enemy event logic.
|
||||
Msg.off('msg_remove_enemy', this.remove.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates a monster generation event.
|
||||
* @returns
|
||||
*/
|
||||
generateEvent () {
|
||||
|
||||
// If the total number of enemies in the scene is greater than or equal to the maximum value No enemy generation logic is performed.
|
||||
// The total number of survivors is equal to the total number of generation minus the number of deaths.
|
||||
if ((this.counter - this.killCounter) >= this.probability.max) return;
|
||||
|
||||
// Get a random value range [0-1].
|
||||
const odds = random();
|
||||
|
||||
// Get a list of weights.
|
||||
const weights = this.probability.weights;
|
||||
|
||||
// Set default occur group index.
|
||||
let occurGroupIndex = -1;
|
||||
|
||||
// Find the matching index in a random list based on a random number.
|
||||
for (let iWeight = 0; iWeight < weights.length; iWeight++) {
|
||||
if (odds <= weights[iWeight]) {
|
||||
occurGroupIndex = iWeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Determine if it is found, if not it means there is a problem with the data configuration.
|
||||
// The set of random intervals contains all interval values from 0 - 1.
|
||||
if (occurGroupIndex === -1) {
|
||||
throw new Error(`Error calculate weight on Level events enemy. value:${odds}`);
|
||||
}
|
||||
|
||||
// Get the maximum value of the current group.
|
||||
const currentMax = this.groupCounter![occurGroupIndex];
|
||||
|
||||
//
|
||||
const weightMax = this.probability.weights_max;
|
||||
|
||||
if (currentMax >= weightMax[occurGroupIndex]) {
|
||||
this._interval = this.probability.interval_weight_max;
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIndex = this.probability.weights_group[occurGroupIndex];
|
||||
const res = DataLevelInst._data.enemies[currentIndex];
|
||||
|
||||
// Send add add enemy.
|
||||
Msg.emit('msg_add_enemy', { res: res, groupID: occurGroupIndex })
|
||||
|
||||
// Increase event count.
|
||||
this.counter++;
|
||||
|
||||
// Increase the mapping event group count.
|
||||
this.groupCounter![occurGroupIndex]++;
|
||||
|
||||
// Send warning message.
|
||||
if (res == 'boss_0') Msg.emit('level_action', 'warning');
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes enemies and updates the corresponding stats.
|
||||
* @param groupIndex Group ID to be removed.
|
||||
*/
|
||||
public remove (groupIndex: number) {
|
||||
|
||||
// Increase the number of kills once
|
||||
this.killCounter++;
|
||||
|
||||
// The number of current index groups minus one.
|
||||
this.groupCounter![groupIndex]--;
|
||||
|
||||
// Exception judgment, if it is less than 0 it means there is a duplicate count of enemy death statistics.
|
||||
// You need to check the death-related logic to see if multiple deaths were executed.
|
||||
if (this.groupCounter![groupIndex] < 0) {
|
||||
throw new Error(`Multiply remove enemy. group index = ${groupIndex}`);
|
||||
}
|
||||
|
||||
// Triggers a death execution message.
|
||||
Msg.emit('msg_kill_enemy', this.killCounter);
|
||||
}
|
||||
|
||||
/**
|
||||
* The detection logic is executed per frame.
|
||||
* @param deltaTime The incremental time of the current frame.
|
||||
* @returns
|
||||
*/
|
||||
update (deltaTime: number) {
|
||||
|
||||
//
|
||||
if (!Level.Instance._isStart && Level.Instance.stop && !Level.Instance._player) return;
|
||||
this._interval -= deltaTime;
|
||||
|
||||
// Interval time less than 0 to start event detection.
|
||||
if (this._interval <= 0) {
|
||||
|
||||
// Get the time of the next decision cycle.
|
||||
this._interval = randomRange(this.probability.interval[0], this.probability.interval[1]);
|
||||
|
||||
// Execute generated events.
|
||||
this.generateEvent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
9
assets/scripts/logic/level/level-events-enemy.ts.meta
Normal file
9
assets/scripts/logic/level/level-events-enemy.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "272c792a-5e63-47d0-8581-1836374665e6",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
118
assets/scripts/logic/level/level-events-items.ts
Normal file
118
assets/scripts/logic/level/level-events-items.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
/*
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://www.cocos.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { _decorator, Component, Node, randomRange, random } from 'cc';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { Level } from './level';
|
||||
import { DataLevelInst } from '../data/data-core';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('LevelEventsItems')
|
||||
export class LevelEventsItems extends Component {
|
||||
|
||||
// Used to record the interval at which time occurs.
|
||||
_interval: number = 0.1;
|
||||
|
||||
// The probability data of item drops.
|
||||
probability: any;
|
||||
|
||||
// Used for the total number of items already generated.
|
||||
counter = 0;
|
||||
|
||||
// Used to count the total number of each grouping.
|
||||
groupCounter: Array<number> | undefined;
|
||||
|
||||
start () {
|
||||
|
||||
// Get probability.
|
||||
this.probability = DataLevelInst._data.probability_drop_items;
|
||||
this.groupCounter = new Array(DataLevelInst._data.items.length);
|
||||
for (let i = 0; i < DataLevelInst._data.items.length; i++) {
|
||||
this.groupCounter[i] = 0;
|
||||
}
|
||||
this._interval = randomRange(this.probability.interval[0], this.probability.interval[1]);
|
||||
Msg.on('msg_remove_item', this.remove.bind(this));
|
||||
}
|
||||
|
||||
onDestroy () {
|
||||
Msg.off('msg_remove_item', this.remove.bind(this));
|
||||
}
|
||||
|
||||
nextEvent () {
|
||||
|
||||
this._interval = randomRange(this.probability.interval[0], this.probability.interval[1]);
|
||||
|
||||
if (this.counter >= this.probability.max) return;
|
||||
|
||||
const odds = random();
|
||||
const weights = this.probability.weights;
|
||||
let occurGroupIndex = -1;
|
||||
|
||||
for (let iWeight = 0; iWeight < weights.length; iWeight++) {
|
||||
if (odds <= weights[iWeight]) {
|
||||
occurGroupIndex = iWeight;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (occurGroupIndex === -1) {
|
||||
throw new Error(`Error calculate weight on Level Infinite Events. value:${odds}`);
|
||||
}
|
||||
|
||||
const currentMax = this.groupCounter![occurGroupIndex];
|
||||
const weightMax = this.probability.weights_max;
|
||||
|
||||
if (currentMax >= weightMax[occurGroupIndex]) {
|
||||
this._interval = this.probability.interval_weight_max;
|
||||
return;
|
||||
}
|
||||
|
||||
const currentIndex = this.probability.weights_group[occurGroupIndex];
|
||||
const res = DataLevelInst._data.items[currentIndex];
|
||||
|
||||
const sendData = { res: res, pos: undefined, groupIndex: currentIndex };
|
||||
Msg.emit('msg_add_item', sendData);
|
||||
this.counter++;
|
||||
this.groupCounter![occurGroupIndex]++;
|
||||
|
||||
}
|
||||
|
||||
public remove (groupIndex: number) {
|
||||
this.counter--;
|
||||
this.groupCounter![groupIndex]--;
|
||||
if (this.groupCounter![groupIndex] < 0) {
|
||||
throw new Error(`Mutiply remove enemy. group index = ${groupIndex}`);
|
||||
}
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
if (!Level.Instance._isStart && Level.Instance.stop && !Level.Instance._player) return;
|
||||
this._interval -= deltaTime;
|
||||
if (this._interval <= 0) {
|
||||
this.nextEvent();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
9
assets/scripts/logic/level/level-events-items.ts.meta
Normal file
9
assets/scripts/logic/level/level-events-items.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a92ca369-3717-4eea-bedd-13a27b811f8b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
42
assets/scripts/logic/level/level-events.ts
Normal file
42
assets/scripts/logic/level/level-events.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import { Level } from './level';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('LevelEvents')
|
||||
export class LevelEvents extends Component {
|
||||
|
||||
_events:{ [key:string]:any } = {};
|
||||
_time = 0;
|
||||
_index = 0;
|
||||
|
||||
_max = 0;
|
||||
_cur:any;
|
||||
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
public init(events:any) {
|
||||
this._events = events;
|
||||
this._index = 0;
|
||||
this._max = this._events.length;
|
||||
|
||||
this._cur = this._events[this._index];
|
||||
}
|
||||
|
||||
updateEvent(deltaTime: number) {
|
||||
|
||||
if (this._index >= this._max) return;
|
||||
|
||||
this._time += deltaTime;
|
||||
|
||||
if (this._time > this._cur.time) {
|
||||
Level.Instance.addEnemy(this._cur.res);
|
||||
this._index++;
|
||||
if (this._index < this._max)
|
||||
this._cur = this._events[this._index];
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
9
assets/scripts/logic/level/level-events.ts.meta
Normal file
9
assets/scripts/logic/level/level-events.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1bf46fc9-4b7a-4723-96bb-450c443b9730",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
366
assets/scripts/logic/level/level.ts
Normal file
366
assets/scripts/logic/level/level.ts
Normal file
@@ -0,0 +1,366 @@
|
||||
/*
|
||||
Copyright (c) 2020-2023 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
https://www.cocos.com/
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
*/
|
||||
|
||||
import { _decorator, Node, find, Vec3, v3, game } from 'cc';
|
||||
import { Action } from '../../core/action/action';
|
||||
import { Save } from '../data/save';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { Singleton } from '../../core/pattern/singleton';
|
||||
import { Res } from '../../core/res/res';
|
||||
import { ResCache } from '../../core/res/res-cache';
|
||||
import { Actor } from '../actor/actor';
|
||||
import { DropItem } from '../item/drop-item';
|
||||
import { NavSystem } from '../navigation/navigation-system';
|
||||
import { DataEquipInst, DataNavigationInst } from '../data/data-core';
|
||||
import { fun } from '../../core/util/fun';
|
||||
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
export class Level extends Singleton {
|
||||
|
||||
// Action objects are used to execute the current set of actions.
|
||||
_action: Action | undefined;
|
||||
|
||||
// Level data object to store static game data.
|
||||
_data: { [key: string]: any } = {};
|
||||
|
||||
// Level time.
|
||||
_time: number = 0;
|
||||
|
||||
// The state at the beginning of the level.
|
||||
_isStart = false;
|
||||
|
||||
// The spawn position of the player's level.
|
||||
_spawn_pos = v3(0, 2, 0);
|
||||
|
||||
// The score rate of the level.
|
||||
_scoreRate: number = 0;
|
||||
|
||||
// The player's game object.
|
||||
_player: Actor | undefined;
|
||||
|
||||
// List of nodes of level enemies.
|
||||
_enemies: Node[] = [];
|
||||
|
||||
// The root node of all objects at game runtime.
|
||||
_objectNode: Node | null | undefined;
|
||||
|
||||
// Current upgrade cards.
|
||||
currentCards: Array<{ name: string; info: any; }> = new Array(3);
|
||||
|
||||
// Level stop.
|
||||
stop = false;
|
||||
|
||||
/**
|
||||
* Initialize the level object.
|
||||
*/
|
||||
public init (): void {
|
||||
|
||||
// Get the level data and copy it for storage.
|
||||
this._data = Object.assign(ResCache.Instance.getJson('data-level').json);
|
||||
|
||||
// Create an action object to manage the action of the level.
|
||||
this._action = new Action('action-level');
|
||||
|
||||
// Find the root node of all objects.
|
||||
this._objectNode = find('init')?.getChildByName('objects');
|
||||
|
||||
// Register external message access function mapping.
|
||||
Msg.on('msg_level_start', this.onLevelStart.bind(this));
|
||||
Msg.on('level_action', this.levelAction.bind(this));
|
||||
Msg.on('level_do', this.do.bind(this));
|
||||
Msg.on('msg_add_enemy', this.addEnemy.bind(this));
|
||||
Msg.on('msg_add_item', this.addDrop.bind(this));
|
||||
Msg.on('msg_replay', this.onReplay.bind(this));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the function with the name specified by the current object.
|
||||
* @param fun Name of the function to be executed.
|
||||
*/
|
||||
public do (fun: string) {
|
||||
this[fun]();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is used to set the behavior related to the start of the level.
|
||||
*/
|
||||
public onLevelStart () {
|
||||
|
||||
// Set level stop is false.
|
||||
this.stop = false;
|
||||
this._isStart = true;
|
||||
|
||||
// Switch to the next statistic.
|
||||
Save.Instance.nextStatistics();
|
||||
|
||||
// Initialize the current path finding data.
|
||||
NavSystem.Init(DataNavigationInst._data);
|
||||
|
||||
this.levelAction('start');
|
||||
}
|
||||
|
||||
public pause () {
|
||||
this.stop = true;
|
||||
}
|
||||
|
||||
public resume () {
|
||||
this.stop = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to restart the game.
|
||||
*/
|
||||
public onReplay () {
|
||||
fun.delay(() => {
|
||||
Msg.emit('push', 'level');
|
||||
}, 2);
|
||||
}
|
||||
|
||||
public levelAction (name: string) {
|
||||
this._action!.on(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Added level role method.
|
||||
* Used to initialize the character game object.
|
||||
*/
|
||||
public addPlayer () {
|
||||
|
||||
// Get a random node from the navigation system.
|
||||
//const point = NavSystem.randomPoint();
|
||||
|
||||
// Get the player's prefab object from the resource cache.
|
||||
const prefab = ResCache.Instance.getPrefab(this._data.prefab_player);
|
||||
|
||||
// Instantiate player level game object.
|
||||
const resPlayer = Res.inst(prefab, this._objectNode!, this._data.spawn_pos);
|
||||
|
||||
// Get the Actor from the player level game object.
|
||||
this._player = resPlayer.getComponent(Actor)!;
|
||||
|
||||
// Detect if this actor exists
|
||||
if (this._player === null) {
|
||||
throw new Error(`Level add player can not bind Actor Component.`);
|
||||
}
|
||||
|
||||
this._player.bulletBox = 5;
|
||||
|
||||
// Set the player tag value of this actor to true.
|
||||
this._player.isPlayer = true;
|
||||
|
||||
// Initialize the player object.
|
||||
this._player.init('data-player');
|
||||
|
||||
// Update player hp.
|
||||
this._player.updateHP();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add level enemy method.
|
||||
* @param res Add enemy resource name.
|
||||
* @param groupID Enemy group id.
|
||||
* @returns Enemy game object.
|
||||
*/
|
||||
public addEnemy (data: { res: string, groupID: number }) {
|
||||
|
||||
// Get a random node from the navigation system.
|
||||
const point = NavSystem.randomPoint();
|
||||
|
||||
// Get the enemy's prefab object from the resource cache.
|
||||
var prefab = ResCache.Instance.getPrefab(data.res);
|
||||
|
||||
// Instantiate enemy level game object.
|
||||
var enemy = Res.inst(prefab, this._objectNode!, point.position);
|
||||
|
||||
enemy.name = data.res;
|
||||
const actor = enemy.getComponent(Actor);
|
||||
if (!actor) {
|
||||
console.error('error inst enemy lose actor component. the name is :', data.res);
|
||||
return;
|
||||
}
|
||||
actor._groupIndex = data.groupID;
|
||||
actor.init(`data-${data.res}`);
|
||||
actor.bulletBox = 9999;
|
||||
actor.isReady = true;
|
||||
this._enemies.push(enemy);
|
||||
return enemy;
|
||||
}
|
||||
|
||||
public removeEnemy (node: Node) {
|
||||
for (let i = 0; i < this._enemies.length; i++) {
|
||||
if (this._enemies[i] === node) {
|
||||
this._enemies.splice(i, 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public addDrop (_data: { res: string, pos: Vec3 | undefined, groupIndex: number }) {
|
||||
if (_data.pos === undefined) {
|
||||
const point = NavSystem.randomPoint();
|
||||
_data.pos = point.position;
|
||||
}
|
||||
const prefab = ResCache.Instance.getPrefab(this._data.prefab_drop_item);
|
||||
const dropNode = Res.inst(prefab, this._objectNode!, _data.pos);
|
||||
dropNode.name = _data.res;
|
||||
|
||||
const drop = dropNode.getComponent(DropItem);
|
||||
const data = DataEquipInst.get(_data.res);
|
||||
|
||||
if (drop === null) {
|
||||
throw new Error(`Drop node can not add component Drop Item.`);
|
||||
}
|
||||
|
||||
drop.init(_data.res, data.drop_effect_index, _data.groupIndex);
|
||||
|
||||
}
|
||||
|
||||
public addObj (res: string) {
|
||||
const point = NavSystem.randomPoint();
|
||||
var prefab = ResCache.Instance.getPrefab(res);
|
||||
var objNode = Res.inst(prefab, this._objectNode!, point.position);
|
||||
return objNode;
|
||||
}
|
||||
|
||||
public update (deltaTime: number): void {
|
||||
if (!this._isStart) return;
|
||||
|
||||
if (this.stop) return;
|
||||
|
||||
this._time += deltaTime;
|
||||
this._action!.update(deltaTime);
|
||||
|
||||
Msg.emit('msg_update_map');
|
||||
}
|
||||
|
||||
/**
|
||||
* Select a skill card to update player attributes.
|
||||
* @param selectIndex Select upgrade card index.
|
||||
*/
|
||||
public upgradePlayerAttributes (selectIndex: number) {
|
||||
// Get upgrade values.
|
||||
const upgradeValues = this.currentCards[selectIndex].info.values;
|
||||
// Upgrade player data.
|
||||
const length = upgradeValues.length;
|
||||
//Update all attributes of the card.
|
||||
for (let i = 0; i < length; i++) {
|
||||
console.log(upgradeValues[i]);
|
||||
const data = upgradeValues[i];
|
||||
this._player!._data[data.name] = data.value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public getUpgradeCardInfo (selectIndex: number) {
|
||||
return this.currentCards[selectIndex].info.describe;
|
||||
}
|
||||
|
||||
public gameOver () {
|
||||
|
||||
// Set level stop is true.
|
||||
this.stop = true;
|
||||
this._isStart = false;
|
||||
Msg.emit('msg_stat_time', { key: 'play', time: this._time });
|
||||
this.calculateScore();
|
||||
this._enemies = [];
|
||||
Save.Instance.saveGameOver(this._time, this._scoreRate);
|
||||
this._player = undefined;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate level score.
|
||||
*/
|
||||
public calculateScore () {
|
||||
|
||||
// Save day.
|
||||
let day = Save.Instance.get('day');
|
||||
if (day === undefined) day = 0;
|
||||
else day++;
|
||||
Save.Instance.setValue('day', day);
|
||||
|
||||
// Get killed number.
|
||||
const killedTimes = Save.Instance.getStatistics('killedTime');
|
||||
|
||||
// Calculate hit rate.
|
||||
const hitBodyTimes = Save.Instance.getStatistics('hit_bodyTimes');
|
||||
const hitHeadTimes = Save.Instance.getStatistics('hit_headTimes');
|
||||
let fireTimes = Save.Instance.getStatistics('fireTimes');
|
||||
const hitRate = fireTimes == 0 ? 0 : ((hitBodyTimes + hitHeadTimes) / fireTimes);
|
||||
Save.Instance.setStatistics('hit_rate', Number(hitRate.toFixed(4)));
|
||||
|
||||
// Calculate be hit times.
|
||||
const beHitBodyTimes = Save.Instance.getStatistics('be_hit_bodyTimes');
|
||||
const beHitHeadTimes = Save.Instance.getStatistics('be_hit_headTimes');
|
||||
const beHitTimes = beHitBodyTimes + beHitHeadTimes;
|
||||
Save.Instance.setStatistics('be_hit_times', beHitTimes);
|
||||
|
||||
// Calculate dodge rate.
|
||||
const enemyFireTimes = Math.max(beHitTimes, Save.Instance.getStatistics('enemy_fireTimes'));
|
||||
const dodgeRate = enemyFireTimes == 0 ? 0 : (1 - beHitTimes / enemyFireTimes);
|
||||
Save.Instance.setStatistics('dodge_rate', Number(dodgeRate.toFixed(4)));
|
||||
|
||||
// Calculate level score.
|
||||
// level score = killed * killed_to_score + hitRate * eachRateValue + dodgeRate * eachRateValue + survivalTime * survival_time_to_score
|
||||
const eachRateValue = this._data.each_rate_value;
|
||||
const level_score = Math.floor(killedTimes * this._data.killed_to_score + hitRate * eachRateValue + dodgeRate * eachRateValue + this._time * this._data.survival_time_to_score);
|
||||
Save.Instance.setStatistics('level_score', level_score);
|
||||
|
||||
// Calculate final score rate.
|
||||
const scoreLevels = this._data.score_level;
|
||||
let passLevel = true;
|
||||
this._scoreRate = scoreLevels.length - 1;
|
||||
for (let i = 0; i < scoreLevels.length; i++) {
|
||||
const infos = scoreLevels[i];
|
||||
passLevel = true;
|
||||
for (let k in infos) {
|
||||
if (k == 'score') continue;
|
||||
if (Save.Instance._currentStatistics[k] < infos[k]) {
|
||||
passLevel = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (passLevel) {
|
||||
this._scoreRate = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Save score rate.
|
||||
Save.Instance.setStatistics('score_rate', this._scoreRate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get final score rating
|
||||
* @returns
|
||||
*/
|
||||
public getLevelScore () {
|
||||
return this._data.score_level[this._scoreRate].score;
|
||||
}
|
||||
|
||||
}
|
||||
9
assets/scripts/logic/level/level.ts.meta
Normal file
9
assets/scripts/logic/level/level.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "de76ffef-f81d-4474-a75f-52ab347c1d33",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
24
assets/scripts/logic/level/spwans-group.ts
Normal file
24
assets/scripts/logic/level/spwans-group.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { _decorator, Component, Node, Camera, director, game, MeshRenderer } from 'cc';
|
||||
import { EDITOR } from 'cc/env';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('SpawnsGroup')
|
||||
export class SpawnsGroup extends Component {
|
||||
|
||||
onEnable() {
|
||||
this.node.children.forEach(child => {
|
||||
const meshRender = child.getComponent(MeshRenderer);
|
||||
if (meshRender) meshRender.enabled = false;
|
||||
});
|
||||
}
|
||||
|
||||
start() {
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
9
assets/scripts/logic/level/spwans-group.ts.meta
Normal file
9
assets/scripts/logic/level/spwans-group.ts.meta
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "aa3b76cd-e02d-4d5b-bb4f-786f54aee4b0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user