[add] first
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
|
||||
import { _decorator, Component, Node, animation, Vec3, v3 } from 'cc';
|
||||
import { Actor } from './actor';
|
||||
import { ActorAnimationGraph } from './actor-animation-graph';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorAnimationGraphGroup')
|
||||
export class ActorAnimationGraphGroup extends Component {
|
||||
|
||||
_groups:ActorAnimationGraph[] | undefined;
|
||||
|
||||
__preload () {
|
||||
this._groups = this.getComponentsInChildren(ActorAnimationGraph);
|
||||
if (this._groups === undefined || this._groups === null) {
|
||||
throw new Error(`${this.node.name} node not find ActorAnimationGraph`);
|
||||
}
|
||||
}
|
||||
|
||||
play (key: string, value: boolean | number) {
|
||||
console.log('ActorAnimationGraphGroup', key, value);
|
||||
for(let i = 0; i < this._groups!.length; i++) {
|
||||
this._groups![i].play(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "a16cf102-2aea-4701-8203-95e21b2ee88b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
import { _decorator, Component, Node, animation, Vec3, v3 } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorAnimationGraph')
|
||||
export class ActorAnimationGraph extends Component {
|
||||
|
||||
_graph: animation.AnimationController | undefined | null;
|
||||
//_actor: Actor = Object.create(null);
|
||||
|
||||
start () {
|
||||
// [3]
|
||||
this._graph = this.getComponent(animation.AnimationController);
|
||||
//this._actor = this.node.parent.parent.getComponent(Actor);
|
||||
|
||||
if (this._graph === undefined || this._graph === null) {
|
||||
throw new Error(`${this.node.name} can not find AnimationController`);
|
||||
}
|
||||
}
|
||||
|
||||
play (key: string, value: boolean | number) {
|
||||
this._graph?.setValue(key, value);
|
||||
}
|
||||
|
||||
setValue(key:string, value:number) {
|
||||
this._graph?.setValue(key, value);
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
// // [4]
|
||||
//this.play('speed', this._actor._data.cur_speed);
|
||||
//this.play('move_speed', this._actor._data.cur_speed + 0.5);
|
||||
//this.play('is_ground', this._actor._data.is_ground);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "fbeac263-42e6-4401-bb66-b6568aca650a",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
import { _decorator, Component, Node, SkeletalAnimation } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorAnimatorController')
|
||||
export class ActorAnimatorController extends Component {
|
||||
|
||||
_anim:SkeletalAnimation = Object.create(null);
|
||||
_data = Object.create(null);
|
||||
|
||||
init(_data) {
|
||||
this._data = _data;
|
||||
this._anim = this.getComponent(SkeletalAnimation);
|
||||
}
|
||||
|
||||
play(name: string) {
|
||||
var anims = this._data[name];
|
||||
this._anim.play(anims[0]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "7acb1d68-3df4-4c3d-8bc4-372796cd91eb",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { _decorator, Component, Node, geometry, PhysicsSystem, PhysicsRayResult } from 'cc';
|
||||
import { fx } from '../../core/effect/fx';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { ActorPart } from './actor-part';
|
||||
import { calculateDamage } from './damage-core';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorAxe')
|
||||
export class ActorAxe extends ActorEquipBase {
|
||||
onFire() {
|
||||
this._bagData!.bulletCount--;
|
||||
const forwardNode = this._actor!._forwardNode;
|
||||
const origin = forwardNode.worldPosition;
|
||||
const dir = forwardNode.forward;
|
||||
let ray = new geometry.Ray(origin.x, origin.y, origin.z, dir.x, dir.y , dir.z);
|
||||
const mask = 1 << 3 | 1 << 4;
|
||||
const distance = this._data.damage.distance;
|
||||
let hit:PhysicsRayResult | undefined = undefined;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, mask, distance)) {
|
||||
hit = PhysicsSystem.instance.raycastClosestResult;
|
||||
}
|
||||
calculateDamage(this._data, hit);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "87a465c0-0b33-4476-9294-4e6acc215b38",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
/*
|
||||
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 { game, _decorator } from 'cc';
|
||||
import { DataEquipInst } from '../data/data-core';
|
||||
import { Level } from '../level/level';
|
||||
import { Actor } from './actor';
|
||||
|
||||
export class ActorBag {
|
||||
|
||||
// The character object to which the current bag belongs.
|
||||
_actor: Actor;
|
||||
|
||||
// Bag capacity.
|
||||
_capacity = 0;
|
||||
|
||||
// Bag usage count statistics.
|
||||
_usageCount = 0;
|
||||
|
||||
constructor (actor: Actor) {
|
||||
|
||||
// The character object to which the current equipment belongs.
|
||||
this._actor = actor;
|
||||
|
||||
// Set Bag capacity.
|
||||
this._capacity = this._actor._data.bag_capacity;
|
||||
|
||||
// Initialize the bag space and set the default value.
|
||||
this._actor._data.equipment_name_list = new Array<string>(this._capacity);
|
||||
for (let i = 0; i < this._capacity; i++) {
|
||||
this._actor._data.equipment_name_list[i] = '';
|
||||
}
|
||||
|
||||
// Set the default value of the bag.
|
||||
const bags = actor._data.bags;
|
||||
for (let i = 0; i < bags.length; i++) {
|
||||
this.pickedItem(bags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the empty slot in the bag
|
||||
* @returns Returns the index of the corresponding empty slot, -1 means it cannot exist.
|
||||
*/
|
||||
public getEmptySlot (): number {
|
||||
|
||||
for (let i = 0; i < this._actor._data.equipment_name_list.length; i++) {
|
||||
const name = this._actor._data.equipment_name_list[i];
|
||||
if (name.length <= 0) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method is check picked same weapon then increase clip.
|
||||
* @param name Weapon name
|
||||
* @returns TRUE is same weapon increase clip, FALSE is not same weapon.
|
||||
*/
|
||||
public pickedSameWeaponIncreaseClips (name: string) {
|
||||
|
||||
const bagItems = this._actor._data.items[name] as BagItem;
|
||||
|
||||
if (bagItems) {
|
||||
|
||||
//bagItems.bulletClipCount++;
|
||||
this._actor.bulletBox++;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* The method picked bullet box.
|
||||
* @returns
|
||||
*/
|
||||
public pickedBulletBox () {
|
||||
|
||||
const bagItems = this._actor._actorEquipment?.currentEquipItem;
|
||||
|
||||
if (bagItems) {
|
||||
|
||||
//bagItems.bulletClipCount++;
|
||||
this._actor.bulletBox += 2;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to pick up item.
|
||||
* @param name Pick item name.
|
||||
* @returns Picked state, true is picked, false is not picked.
|
||||
*/
|
||||
public pickedItem (name: string): boolean {
|
||||
|
||||
// Get the current backpack item by name, may be empty.
|
||||
let bagItems = this._actor._data.items[name];
|
||||
|
||||
// Get information about equipment props.
|
||||
const equipData = DataEquipInst.get(name);
|
||||
|
||||
// If the item already exists in the backpack and is stackable, run the stacking logic.
|
||||
if (bagItems && equipData.stackable) {
|
||||
this.stackItem(equipData.bullet_count);
|
||||
} else {
|
||||
|
||||
// Get the current empty slot index and determine if it exists.
|
||||
const index = this.getEmptySlot();
|
||||
if (index === -1) return false;
|
||||
|
||||
// Create a backpack item.
|
||||
this.createItem(equipData, name);
|
||||
|
||||
// Update the value corresponding to the array index of the item slot
|
||||
this._actor._data.equipment_name_list[index] = name;
|
||||
|
||||
// Accumulation of the number of uses.
|
||||
this._usageCount++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to discard the equipment currently held in your hand.
|
||||
* @returns Dropped status: true is success false is failure
|
||||
*/
|
||||
public dropItem (): boolean {
|
||||
|
||||
// Get the bag index of the currently held equipment.
|
||||
const curIndex = this._actor._data.current_equipment_index;
|
||||
|
||||
// Determine if the current index is in the valid range.
|
||||
// Default 0 equipment is not drop.
|
||||
if (curIndex >= this._capacity || curIndex <= 0) return false;
|
||||
|
||||
// Get it from the bag equipment list.
|
||||
const data = this._actor._data.equipment_name_list;
|
||||
|
||||
//Get the name of the equipment in the current slot and determine if the equipment exists based on the name value.
|
||||
const name = data[curIndex];
|
||||
if (!name || name.length <= 0) return false;
|
||||
|
||||
// Take off the prop that is being equipped.
|
||||
this._actor._actorEquipment?.unEquip();
|
||||
|
||||
// Clear specific items data.
|
||||
this._actor._data.items[name] = undefined;
|
||||
|
||||
// Discard the current prop near the character.
|
||||
const pos = this._actor.node.worldPosition;
|
||||
Level.Instance.addDrop(name, pos);
|
||||
|
||||
// Clears the value of the equipment list for the current index mapping.
|
||||
data[curIndex] = '';
|
||||
|
||||
// Replace the default equipment.
|
||||
this._actor._data.current_equipment_index = 0;
|
||||
this._actor._actorEquipment?.equip(0);
|
||||
|
||||
// The total number of bags used decreases.
|
||||
this._usageCount--;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to create bag items.
|
||||
* @param equipData Current equip data.
|
||||
* @param name The name of the bag prop that needs to be created.
|
||||
*/
|
||||
public createItem (equipData: any, name: string) {
|
||||
|
||||
let newItems = {
|
||||
'name': name,
|
||||
'actor': this._actor,
|
||||
'stackable': equipData.stackable === undefined ? false : true,
|
||||
'count': 1,
|
||||
'data': equipData,
|
||||
'bulletClipCount': equipData.bullet_clip_count,
|
||||
'bulletCount': equipData.bullet_count,
|
||||
'lastUseTime': game.totalTime,
|
||||
}
|
||||
|
||||
this._actor._data.items[name] = newItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is used to update the number of stackable props.
|
||||
* @param bagItems Current backpack information.
|
||||
*/
|
||||
public stackItem (bagItems: BagItem) {
|
||||
bagItems.count++;
|
||||
bagItems.bulletCount += bagItems.data.bullet_count;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
// The bag item data interface.
|
||||
export interface BagItem {
|
||||
fov: number;
|
||||
name: string,
|
||||
actor: Actor,
|
||||
stackable: boolean,
|
||||
count: number,
|
||||
bulletClipCount: number
|
||||
bulletCount: number,
|
||||
data: any,
|
||||
lastUseTime: number,
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "31aaecab-d831-4c7e-b31f-cc0c0d40b35e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,420 @@
|
||||
/*
|
||||
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, Vec3, v3, random, randomRangeInt, Node, math, game, randomRange, Game } from 'cc';
|
||||
import { SensorRaysAngle } from '../../core/sensor/sensor-rays-angle';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
import { NavSystem } from '../navigation/navigation-system';
|
||||
import { ActorInputBrain } from './actor-input-brain';
|
||||
import { Level } from '../level/level';
|
||||
import { Actor } from './actor';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
let tempRotationSideVector = v3(0, 0, 0);
|
||||
|
||||
@ccclass('ActorBrain')
|
||||
export class ActorBrain extends Component {
|
||||
|
||||
// The character object to which the current equipment belongs.
|
||||
_actor: Actor | undefined;
|
||||
|
||||
// The currently planned waypoint.
|
||||
_wayPoints = new Array<NavSystem.NavPointType>();
|
||||
|
||||
// The direction the character moves.
|
||||
_moveDir: Vec3 = v3(0, 0, 0);
|
||||
|
||||
// Target distance from my direction.
|
||||
targetDirection: Vec3 = v3(0, 0, 0);
|
||||
|
||||
// Unified input management object for character.
|
||||
input: ActorInputBrain | undefined;
|
||||
|
||||
// Sector sensor, used to detect the status in front of the character.
|
||||
sensorRays: SensorRaysAngle | undefined;
|
||||
|
||||
// Whether it is waypoint navigation.
|
||||
isFollowWayPointsMove = false;
|
||||
|
||||
// current waypoint index.
|
||||
currentWaypointsIndex = 1;
|
||||
|
||||
// The closest Navigation point marker to the character.
|
||||
closestNavigationPon = -1;
|
||||
|
||||
// The target object node.
|
||||
_targetNode: Node | undefined;
|
||||
|
||||
// The position of the target node.
|
||||
targetPosition: Vec3 = v3(0, 0, 0);
|
||||
|
||||
// Path node index of the current fire.
|
||||
waypointsFireIndex = -1;
|
||||
|
||||
// Open fire planning waypoints.
|
||||
waypointsFire = new Array<NavSystem.NavPointType>();
|
||||
|
||||
// The direction of the open fire.
|
||||
fireDirection = v3(0, 0, 0);
|
||||
|
||||
// The time it takes to replace ammunition.
|
||||
reloadTime = 0;
|
||||
|
||||
// Path following direction, 1 means move forward, -1 means move backwards.
|
||||
followPathsDirection = 1;
|
||||
|
||||
// Fire wait time.
|
||||
fireWaitTime = 5;
|
||||
|
||||
start () {
|
||||
this._actor = this.getComponent(Actor)!;
|
||||
this.input = this.getComponent(ActorInputBrain)!;
|
||||
const sensorNode = this.node.getChildByName('sensor_target')!;
|
||||
this.sensorRays = sensorNode.getComponent(SensorRaysAngle)!;
|
||||
this.closestNavigationPon = this._actor._data.nearest;
|
||||
|
||||
if (this._actor === undefined || this.input === undefined || this.sensorRays === undefined) {
|
||||
throw new Error(`${this.node.name} node lose components : ActorBase or ActorInputBrain.`);
|
||||
}
|
||||
}
|
||||
|
||||
onMove () {
|
||||
this.input!.onMove(this._moveDir);
|
||||
this.input!.onRotation(this.targetDirection.x, this.targetDirection.z);
|
||||
this.input!.onRun(random() < 0.05);
|
||||
}
|
||||
|
||||
onJump () {
|
||||
this.input?.onJump();
|
||||
}
|
||||
|
||||
onCrouch () {
|
||||
this.input?.onCrouch();
|
||||
}
|
||||
|
||||
onProne () {
|
||||
this.input?.onProne();
|
||||
}
|
||||
|
||||
onFire () {
|
||||
this.input?.onFire();
|
||||
}
|
||||
|
||||
update (deltaTime: Number) {
|
||||
|
||||
if (Level.Instance.stop) return;
|
||||
|
||||
// Not ready returns do not execute the following logic.
|
||||
if (!this._actor!.isReady) return;
|
||||
|
||||
// If you die, you will return without executing the logic of your brain.
|
||||
if (this._actor?._data.is_dead) return;
|
||||
|
||||
// Returns without executing brain logic if the player dies.
|
||||
const player = Level.Instance._player;
|
||||
if (!player || Level.Instance._player?._data.is_dead) return;
|
||||
|
||||
// Check near has player.
|
||||
this.checkNearPlayer();
|
||||
|
||||
// Test to open and close enemy fire.
|
||||
//this._targetNode = undefined;
|
||||
|
||||
//console.log('target node:', this._targetNode);
|
||||
|
||||
// Go target position.
|
||||
|
||||
// Find target look at target and shoot.
|
||||
if (this._targetNode !== undefined) {
|
||||
this.shootFire();
|
||||
} else { // Random move and find target.
|
||||
this.randomMove();
|
||||
this.input?.onAim(false);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
shootFire () {
|
||||
|
||||
|
||||
const angle = this.checkPlayerAngle();
|
||||
|
||||
// Fire move.
|
||||
this.moveFire(angle);
|
||||
|
||||
// Wait reload weapon.
|
||||
if (this.reloadTime > 0) {
|
||||
this.reloadTime -= game.deltaTime;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check bullet empty.
|
||||
if (this._actor?._actorEquipment?.currentEquip?.isBulletEmpty) {
|
||||
// Reload Bullet.
|
||||
this.input?.onReload();
|
||||
this.reloadTime = 3;
|
||||
return;
|
||||
}
|
||||
|
||||
this.input?.onAim(true);
|
||||
|
||||
// Check fire.
|
||||
this.checkFire(angle);
|
||||
|
||||
}
|
||||
|
||||
randomMove () {
|
||||
this.waypointsFireIndex = -1;
|
||||
if (this.isFollowWayPointsMove) {
|
||||
this.PathsFollowing();
|
||||
} else {
|
||||
this.calculateNextPosition();
|
||||
}
|
||||
}
|
||||
|
||||
PathsFollowing () {
|
||||
|
||||
if (this.isFollowWayPointsMove) {
|
||||
|
||||
this._actor!._actorMove!.faceMove = true;
|
||||
|
||||
const worldPosition = this._actor!.node.worldPosition;
|
||||
const target = this._wayPoints[this.currentWaypointsIndex];
|
||||
|
||||
UtilVec3.copy(this.targetPosition, target);
|
||||
|
||||
// Detect distance to target point.
|
||||
if (Vec3.distance(worldPosition, target) <= 1) {
|
||||
|
||||
// Arrive current node.
|
||||
this.currentWaypointsIndex += this.followPathsDirection;
|
||||
|
||||
if (this.currentWaypointsIndex >= this._wayPoints.length || this.currentWaypointsIndex < 0) this.isFollowWayPointsMove = false;
|
||||
else this.closestNavigationPon = this._wayPoints[this.currentWaypointsIndex].id;
|
||||
|
||||
} else {
|
||||
|
||||
// Detects if there is a character ahead.
|
||||
if (this.sensorRays?.checkedNode) {
|
||||
//this.followPathsDirection = -1;
|
||||
|
||||
//Calculate checked node direction.
|
||||
UtilVec3.copy(this.targetDirection, this.sensorRays.checkedNode.worldPosition);
|
||||
this.targetDirection.y = this.node.worldPosition.y;
|
||||
|
||||
let checkDirection = this.targetDirection.clone();
|
||||
checkDirection.subtract(this.node.worldPosition);
|
||||
|
||||
//Calculate checked node side.
|
||||
const side = -this.targetDirection.clone().cross(this.node.forward).y
|
||||
|
||||
//Calculate normal vector.
|
||||
let normal = checkDirection.clone();
|
||||
normal.cross(side > 0 ? v3(0, -1, 0) : v3(0, 1, 0));
|
||||
|
||||
//Calculate new target direction.
|
||||
this.targetDirection.add(normal);
|
||||
|
||||
} else {
|
||||
|
||||
// Calculate move direction.
|
||||
UtilVec3.copy(this.targetDirection, this.targetPosition);
|
||||
|
||||
//this.followPathsDirection = 1;
|
||||
}
|
||||
|
||||
this.targetDirection.y = worldPosition.y;
|
||||
this.targetDirection.subtract(worldPosition).normalize();
|
||||
|
||||
// Set target move.
|
||||
this._moveDir.x = -this.targetDirection.x;
|
||||
this._moveDir.y = 0;
|
||||
this._moveDir.z = -this.targetDirection.z;
|
||||
|
||||
// Calculates the rotation angle of the target.
|
||||
this.lookAtTarget(this._moveDir);
|
||||
|
||||
this._moveDir.x = 0;
|
||||
this._moveDir.y = 0;
|
||||
this._moveDir.z = 1;
|
||||
|
||||
//
|
||||
this.onMove();
|
||||
|
||||
// Random Jump.
|
||||
//if (random() < 0.05) this.onJump();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
followTargetPaths () {
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
lookAtTarget (lookAtDirection: Vec3) {
|
||||
|
||||
UtilVec3.copy(tempRotationSideVector, lookAtDirection);
|
||||
const angle = Math.abs(Vec3.angle(lookAtDirection, this.node.forward));
|
||||
if (angle > 0.001) {
|
||||
const side = Math.sign(-tempRotationSideVector.cross(this.node.forward).y);
|
||||
this.targetDirection.x = side * angle;// game.deltaTime;
|
||||
this.targetDirection.z = 0;
|
||||
}
|
||||
}
|
||||
|
||||
moveFire (angle: number) {
|
||||
|
||||
this._actor!._actorMove!.faceMove = false;
|
||||
|
||||
if (this.waypointsFireIndex === -1) {
|
||||
this.closestNavigationPon = NavSystem.findNearest(this._actor!.node.worldPosition);
|
||||
NavSystem.randomFirePath(this.waypointsFire, this.closestNavigationPon);
|
||||
this.waypointsFireIndex = 0;
|
||||
}
|
||||
|
||||
const worldPosition = this._actor!.node.worldPosition;
|
||||
let target = this.waypointsFire![this.waypointsFireIndex];
|
||||
|
||||
UtilVec3.copy(this.targetPosition, target);
|
||||
|
||||
const targetDistance = Vec3.distance(worldPosition, target);
|
||||
|
||||
//console.log('target distance:', targetDistance);
|
||||
|
||||
if (targetDistance <= 1) {
|
||||
|
||||
// Next way
|
||||
this.waypointsFireIndex++;
|
||||
|
||||
if (this.waypointsFireIndex >= this.waypointsFire!.length) {
|
||||
|
||||
this.closestNavigationPon = NavSystem.findNearest(this._actor!.node.worldPosition);
|
||||
NavSystem.randomFirePath(this.waypointsFire, this.closestNavigationPon);
|
||||
this.waypointsFireIndex = 0;
|
||||
target = this.waypointsFire![this.waypointsFireIndex];
|
||||
}
|
||||
}
|
||||
|
||||
// Calculate move direction.
|
||||
UtilVec3.copy(this.targetDirection, this.targetPosition);
|
||||
this.targetDirection.y = worldPosition.y;
|
||||
|
||||
// Calculate angle speed.
|
||||
const angleSpeed = angle < 5 ? 0.5 : 1.5;
|
||||
this.targetDirection.subtract(worldPosition).normalize().multiplyScalar(angleSpeed);
|
||||
|
||||
this._moveDir.x = -this.targetDirection.x;
|
||||
this._moveDir.y = 0;
|
||||
this._moveDir.z = -this.targetDirection.z;
|
||||
|
||||
// Look at direction.
|
||||
const player = Level.Instance._player;
|
||||
|
||||
UtilVec3.copy(this.targetDirection, worldPosition);
|
||||
|
||||
this.targetDirection.y += player._data.is_crouch ? 0.3 : 1;
|
||||
this.targetDirection.subtract(player.node.worldPosition).normalize();
|
||||
this.lookAtTarget(this.targetDirection);
|
||||
|
||||
this.onMove();
|
||||
|
||||
//if (random() < 0.1) this.onJump();
|
||||
|
||||
}
|
||||
|
||||
checkPlayerAngle (): number {
|
||||
|
||||
// Check shoot angle.
|
||||
const player = Level.Instance._player;
|
||||
const forward = this._actor?.node.forward!;//this._actor?._forwardNode!.forward!;
|
||||
UtilVec3.copy(this.fireDirection, player.node!.worldPosition);
|
||||
this.fireDirection.subtract(this._actor!.node.worldPosition);
|
||||
const angle = math.toDegree(Vec3.angle(forward, this.fireDirection));
|
||||
|
||||
return angle;
|
||||
}
|
||||
|
||||
checkFire (angle: number) {
|
||||
|
||||
// Brain wait fire logic.
|
||||
this.fireWaitTime -= game.deltaTime;
|
||||
if (this.fireWaitTime > 0) return;
|
||||
this.fireWaitTime = randomRange(0.3, 1.3);
|
||||
|
||||
if (angle < 10) this.onFire();
|
||||
}
|
||||
|
||||
freePathMove () {
|
||||
NavSystem.randomPaths(this._wayPoints, this._actor!.node.worldPosition, randomRangeInt(5, 10), this.closestNavigationPon);
|
||||
//Navigation.calculateRandomPoint(this._actor!.node.worldPosition);
|
||||
console.log('this._wayPoints:', this._wayPoints);
|
||||
this.isFollowWayPointsMove = true;
|
||||
this.currentWaypointsIndex = 0;
|
||||
}
|
||||
|
||||
fleeTarget () {
|
||||
// calculate flee.
|
||||
}
|
||||
|
||||
followTarget () {
|
||||
// calculate target.
|
||||
NavSystem.findPaths(this._wayPoints, this._actor!.node.worldPosition, this.closestNavigationPon, Level.Instance._player!.node.worldPosition);
|
||||
|
||||
}
|
||||
|
||||
checkNearPlayer () {
|
||||
|
||||
const player = Level.Instance._player;
|
||||
|
||||
if (!player) return undefined;
|
||||
|
||||
const data = this._actor!._data;
|
||||
const distance = Vec3.distance(player.node.worldPosition, this._actor!.node.worldPosition);
|
||||
|
||||
//console.log('target distance:', distance, ' nearby distance:', data._ai_nearby_distance);
|
||||
|
||||
if (distance < data.ai_nearby_distance) {
|
||||
this._targetNode = player.node;
|
||||
} else {
|
||||
this._targetNode = undefined;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
calculateNextPosition () {
|
||||
NavSystem.randomPaths(this._wayPoints, this._actor!.node.worldPosition, randomRangeInt(5, 10),);
|
||||
if (this._wayPoints.length === 0) {
|
||||
console.warn(`${this.node.name} can not find path`);
|
||||
return;
|
||||
}
|
||||
//console.log('this._wayPoints:', this._wayPoints);
|
||||
this.isFollowWayPointsMove = true;
|
||||
this.currentWaypointsIndex = 0;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "2fdf616d-2091-4a60-9880-0cc718e65277",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { IActorEquip } from './actor-interface';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorCrossbow')
|
||||
export class ActorCrossbow extends ActorEquipBase {
|
||||
|
||||
_pointShoot:Node | null | undefined;
|
||||
|
||||
start() {
|
||||
this._pointShoot = this.node.getChildByName('point_shoot');
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "5e88b1bb-ad7a-4f42-9417-33d45cb61159",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
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 } from 'cc';
|
||||
import { geometry, PhysicsSystem, PhysicsRayResult } from 'cc';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { calculateDamage } from './damage-core';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
const { ccclass, property } = _decorator;
|
||||
let ray = new geometry.Ray();
|
||||
|
||||
@ccclass('ActorEnemyGun')
|
||||
export class ActorEnemyGun extends ActorEquipBase {
|
||||
|
||||
/**
|
||||
* Execute fire.
|
||||
*/
|
||||
onFire() {
|
||||
|
||||
if(!this._actor?._forwardNode) return;
|
||||
|
||||
// The number of bullets is reduced by one.
|
||||
this._bagData!.bulletCount--;
|
||||
|
||||
// Get the weapon shooting direction node.
|
||||
const forwardNode = this._actor!._forwardNode!;
|
||||
|
||||
// Get Weapon Shooting Points
|
||||
const origin = forwardNode.worldPosition;
|
||||
|
||||
// Get the weapon shooting direction.
|
||||
const shootDirection = forwardNode.forward;
|
||||
|
||||
// Set physical ray detection parameters.
|
||||
UtilVec3.copy(ray.o, origin);
|
||||
UtilVec3.copy(ray.d, shootDirection);
|
||||
|
||||
// Get weapon range.
|
||||
const distance = this._data.damage.distance;
|
||||
|
||||
// Start physical shot detection.
|
||||
let hit:PhysicsRayResult | undefined;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, this.mask, distance)) {
|
||||
hit = PhysicsSystem.instance.raycastClosestResult;
|
||||
}
|
||||
|
||||
// Show Tracer line.
|
||||
this.showTracer(hit, shootDirection);
|
||||
|
||||
// Calculates shot damage.
|
||||
calculateDamage(this._data, hit);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e36354c3-abb6-4a73-b240-9b6229d711e4",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
/*
|
||||
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, game, Vec3, PhysicsRayResult, randomRange, v3 } from 'cc';
|
||||
import { ActionActorEquip, key_type_boolean } from '../../core/action/action';
|
||||
import { Actor } from './actor';
|
||||
import { BagItem } from './actor-bag';
|
||||
import { UtilNode, UtilVec3 } from '../../core/util/util';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { ActorAnimationGraph } from './actor-animation-graph';
|
||||
import { FxBase } from '../../core/effect/fx-base';
|
||||
import { fx } from '../../core/effect/fx';
|
||||
import { Local } from '../../core/localization/local';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
let tracerEndPosition = v3(0, 0, 0);
|
||||
|
||||
@ccclass('ActorEquipBase')
|
||||
export class ActorEquipBase extends Component {
|
||||
|
||||
point_shoot: Node | undefined;
|
||||
|
||||
_animationGraph: ActorAnimationGraph | undefined;
|
||||
|
||||
_view: Node | undefined;
|
||||
|
||||
_bagData: BagItem | undefined;
|
||||
|
||||
_data: { [key: string]: any } = {};
|
||||
|
||||
_action: ActionActorEquip | undefined;
|
||||
|
||||
_actor: Actor | undefined;
|
||||
|
||||
isPlayer = false;
|
||||
|
||||
fxMuzzle: FxBase | undefined;
|
||||
|
||||
isBulletEmpty = false;
|
||||
|
||||
mask = 1 << 2 | 1 << 3 | 1 << 4;
|
||||
|
||||
__preload () {
|
||||
this.point_shoot = this.node.getChildByName('point_shoot')!;
|
||||
this.fxMuzzle = UtilNode.find(this.node, 'fx_muzzle').getComponent(FxBase)!;
|
||||
this._view = this.node.getChildByName('view')!;
|
||||
this.node.on('do', this.do, this);
|
||||
this.node.on('init', this.init, this);
|
||||
}
|
||||
|
||||
|
||||
init (bagData: BagItem) {
|
||||
this._actor = bagData.actor;
|
||||
this._bagData = bagData;
|
||||
this._data = this._bagData.data;
|
||||
this._action = new ActionActorEquip(this._data.action, this);
|
||||
this._bagData.lastUseTime = game.totalTime / 1000;
|
||||
this.isPlayer = this._actor.isPlayer;
|
||||
this._animationGraph = this._actor._animationGraph;
|
||||
}
|
||||
|
||||
onDestroy () {
|
||||
this.node.off('do', this.do, this);
|
||||
this.node.off('init', this.init, this);
|
||||
}
|
||||
|
||||
do (name: string) {
|
||||
if (this._action) {
|
||||
if (name === 'fire' && !this.checkUse()) return;
|
||||
this._action.on(name);
|
||||
}
|
||||
}
|
||||
|
||||
checkAutoFire () {
|
||||
if (this._actor?._data.is_auto_fire) {
|
||||
this.do("fire");
|
||||
}
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
this._action?.update(deltaTime);
|
||||
}
|
||||
|
||||
setActive (data: key_type_boolean) {
|
||||
const activeNode = this.node.getChildByName(data.key);
|
||||
if (activeNode) activeNode.active = data.value;
|
||||
else console.warn(` You want set undefined node active. ${this.node?.name}/${data.key}`);
|
||||
}
|
||||
|
||||
hiddenNode () {
|
||||
this.node.active = false;
|
||||
}
|
||||
|
||||
setFx (data: key_type_boolean) {
|
||||
fx.playLoop(this.node, data.key, data.value);
|
||||
}
|
||||
|
||||
showMuzzle () { this.fxMuzzle?.play(); }
|
||||
|
||||
onFx (name: string) {
|
||||
fx.play(this.node, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Weapon recoil method
|
||||
*/
|
||||
onRecoil () {
|
||||
|
||||
// Get the recoil ratio.
|
||||
// Aim state gets a specific value based on the gun's data, non-Aim state defaults to one.
|
||||
const recoil_rate = this._actor!._data.is_aim ? this._data.recoil_aim_rate : 1;
|
||||
|
||||
// Random recoil offset is performed.
|
||||
const recoilX = randomRange(this._data.recoil_x[0], this._data.recoil_x[1]) * recoil_rate;
|
||||
const recoilY = randomRange(this._data.recoil_y[0], this._data.recoil_y[1]) * recoil_rate;
|
||||
|
||||
// Set the offset of the recoil.
|
||||
this._actor?.onRotation(recoilX, recoilY);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display the current infrared tracking path.
|
||||
* @param hit The location of the detection point.
|
||||
* @param dir The direction of the target point.
|
||||
*/
|
||||
showTracer (hit: PhysicsRayResult | undefined, dir: Vec3) {
|
||||
|
||||
// Get the world coordinates of the firing point.
|
||||
const origin = this.fxMuzzle!.node.worldPosition;
|
||||
|
||||
// The physical hit point exists set as the end coordinate.
|
||||
if (hit?.hitPoint) {
|
||||
UtilVec3.copy(tracerEndPosition, hit.hitPoint);
|
||||
} else { // If the physical hit point does not exist, the end point is extended by 100 units in the direction of fire.
|
||||
UtilVec3.copy(tracerEndPosition, origin);
|
||||
tracerEndPosition.add3f(dir.x * 100, dir.y * 100, dir.z * 100);
|
||||
}
|
||||
//console.log(origin, dir, tracerEndPosition);
|
||||
Msg.emit('msg_set_tracer', { start: origin, end: tracerEndPosition });
|
||||
}
|
||||
|
||||
actionEnd () { }
|
||||
|
||||
checkUse (): boolean {
|
||||
// Check bullet count.
|
||||
this.isBulletEmpty = this._bagData!.bulletCount <= 0 && this._bagData!.data.bullet_count !== -1;
|
||||
if (this.isBulletEmpty) {
|
||||
this.do('fire_empty');
|
||||
return false;
|
||||
}
|
||||
const lastUseTime = this._bagData!.lastUseTime;
|
||||
const timeSpace = (game.totalTime - lastUseTime) / 1000;
|
||||
return timeSpace >= this._data.damage.cooling;
|
||||
|
||||
}
|
||||
|
||||
updateCooling () {
|
||||
this._bagData!.lastUseTime = game.totalTime;
|
||||
}
|
||||
|
||||
checkFullBullet (): boolean {
|
||||
|
||||
if (this._bagData!.bulletCount == this._bagData?.data.bullet_count) {
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('bullet_is_full')}`
|
||||
);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
onReload () {
|
||||
|
||||
if (!this._actor) return;
|
||||
|
||||
if (this._actor.bulletBox > 0) {
|
||||
//this._bagData!.bulletClipCount--;
|
||||
this._actor.bulletBox--;
|
||||
if (this._actor.bulletBox < 0) this._actor.bulletBox = 0;
|
||||
this._bagData!.bulletCount = this._bagData?.data.bullet_count;
|
||||
this.isBulletEmpty = false;
|
||||
} else {
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('clip_is_null')}`
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onUse () { }
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e03a7900-64f1-4336-a4db-b663c4a6628b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,227 @@
|
||||
/*
|
||||
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, game, math } from 'cc';
|
||||
import { Msg } from "../../core/msg/msg";
|
||||
import { Res } from '../../core/res/res';
|
||||
import { ResCache } from '../../core/res/res-cache';
|
||||
import { UtilNode } from '../../core/util/util';
|
||||
import { Actor } from "./actor";
|
||||
import { BagItem } from './actor-bag';
|
||||
import { fun } from '../../core/util/fun';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
|
||||
|
||||
export class ActorEquipment {
|
||||
|
||||
// The character object to which the current equipment belongs.
|
||||
_actor:Actor;
|
||||
|
||||
// A pool of cached equipment objects.
|
||||
//The purpose is to avoid the creation and destruction of objects at runtime.
|
||||
equipPool:{ [key:string]:Node } = {};
|
||||
|
||||
// Dictionary of weapon skeleton nodes for mounted equipment.
|
||||
equipBoneNode: { [key:string]:Node } = {};
|
||||
|
||||
// Current equipment node.
|
||||
currentEquipNode:Node | undefined;
|
||||
|
||||
// bag information of current equipment.
|
||||
currentEquipItem:BagItem | undefined;
|
||||
|
||||
// The component object of the current weapon.
|
||||
currentEquip:ActorEquipBase | undefined;
|
||||
|
||||
// The stability value of the equipment.
|
||||
// The purpose is to describe the stability value of the shot.
|
||||
// This value affects the size of the aiming area of the shot.
|
||||
stableValue = 1;
|
||||
|
||||
constructor(actor:Actor) {
|
||||
|
||||
// Initialize the Actor object corresponding to the equipment manager passed in.
|
||||
this._actor = actor;
|
||||
|
||||
// Get all node maps with the name 'weapon_root'.
|
||||
this.equipBoneNode = UtilNode.getChildrenByNameBlur(this._actor.node, 'weapon_root');
|
||||
|
||||
// Initialize the cache pool for the equipment list.
|
||||
const equipmentList = this._actor._data.cache_equipment_list;
|
||||
|
||||
// Get the length of the equipment list.
|
||||
const length = equipmentList.length;
|
||||
for(let i = 0; i < length; i++) {
|
||||
|
||||
// Get the equipment name from the current index.
|
||||
const weaponName = equipmentList[i];
|
||||
|
||||
// Get the prefab of the equipment from the resource buffer pool.
|
||||
const prefab = ResCache.Instance.getPrefab(weaponName + '_tps');
|
||||
|
||||
// Get the bone node of the equipment.
|
||||
const bindNode = this.equipBoneNode[this._actor._data.weapon_bone];
|
||||
|
||||
// Instantiate the game object and set the parent node to the bone node.
|
||||
const nodePrefab = Res.inst(prefab, bindNode);
|
||||
|
||||
// Set the object pool key to map to this instantiated weapon object.
|
||||
this.equipPool[weaponName] = nodePrefab;
|
||||
|
||||
// Set the activity of this cache object to false.
|
||||
nodePrefab.active = false;
|
||||
}
|
||||
|
||||
// Equip the default weapon.
|
||||
this.equip(actor._data.default_equip_index);
|
||||
}
|
||||
|
||||
public equip(replaceEquipmentIndex:number):boolean {
|
||||
|
||||
// Get the current bag equipment index.
|
||||
const currentEquipmentIndex = this._actor._data.current_equipment_index;
|
||||
|
||||
// If the current bag index is the same as the updated bag index,
|
||||
// true is no need to switch weapons, false is need to switch weapons.
|
||||
if (currentEquipmentIndex !== replaceEquipmentIndex) {
|
||||
|
||||
// Get bag equipment name list from player data.
|
||||
const equipment_name_list = this._actor._data.equipment_name_list;
|
||||
|
||||
// Get the name of the equipment name to be switched from the equipment list.
|
||||
const changeEquipmentName = equipment_name_list[replaceEquipmentIndex];
|
||||
|
||||
// Return false if the equipment does not exist or is not empty to cancel switching equipment.
|
||||
if(!changeEquipmentName || changeEquipmentName.length <= 0) return false;
|
||||
|
||||
// Uninstall current equipment.
|
||||
this.unEquip();
|
||||
|
||||
// Replace new equipment data and models.
|
||||
// Here you need to do a time delay with the animation.
|
||||
const self = this;
|
||||
fun.delay(()=>{
|
||||
const items = self._actor._data.items;
|
||||
self.currentEquipNode = self.equipPool[changeEquipmentName];
|
||||
self.currentEquipItem = items[changeEquipmentName];
|
||||
self.currentEquipNode!.active = true;
|
||||
self.currentEquipNode!.emit('init',this.currentEquipItem);
|
||||
self.currentEquipNode!.emit('do', 'take_out');
|
||||
self._actor._data.current_equipment_index = replaceEquipmentIndex;
|
||||
self.currentEquip = self.currentEquipNode?.getComponent(ActorEquipBase)!;
|
||||
if(this._actor.isPlayer) {
|
||||
//const mainCamera = CameraSetting.main?.camera;
|
||||
//if(mainCamera) mainCamera.fov = this.currentEquipItem!.fov;
|
||||
Msg.emit('msg_change_equip');
|
||||
Msg.emit('msg_update_equip_info');
|
||||
}
|
||||
}, 0.3)
|
||||
|
||||
return true;
|
||||
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninstall current equipment.
|
||||
*/
|
||||
public unEquip() {
|
||||
|
||||
//Get the index of the current equipment.s
|
||||
const currentEquipmentIndex = this._actor._data.current_equipment_index;
|
||||
|
||||
// Compare whether the current equipment index value is -1.
|
||||
// An index of -1 means no current equipment, skip setting.
|
||||
if (currentEquipmentIndex !== -1) {
|
||||
|
||||
// Get a list of equipment names.
|
||||
const equipment_name_list = this._actor._data.equipment_name_list;
|
||||
|
||||
// Get the current equipment name.
|
||||
const currentEquipmentName = equipment_name_list[currentEquipmentIndex];
|
||||
|
||||
// Whether the current equipment name exists.
|
||||
// false means it does not exist, the return function does not uninstall the equipment
|
||||
if(!currentEquipmentName) {
|
||||
console.warn(`The equipment index that does not exist, the index id is ${currentEquipmentIndex}, the object is ${this._actor?.node.name}`)
|
||||
return;
|
||||
}
|
||||
|
||||
// Get the current equipment object node from the equipment object pool.
|
||||
const currentEquipmentNode = this.equipPool[currentEquipmentName];
|
||||
|
||||
//Whether the object pool contains equipment objects.
|
||||
if (currentEquipmentNode) {
|
||||
// Notify the equipment node to perform recovery behavior.
|
||||
currentEquipmentNode.emit('do', 'take_back');
|
||||
}else{
|
||||
console.warn(``);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execution equipment action.
|
||||
* @param action Name of the execution action.
|
||||
*/
|
||||
public do(action:string) {
|
||||
// Execute the current equipment action.
|
||||
this.currentEquipNode?.emit('do', action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update to set the range of Aim.
|
||||
* @param normalizeCharacterMoveSpeed Normalized character movement speed.
|
||||
* @param toMax The aim range is set to the maximum value: true is set, false is not set.
|
||||
*/
|
||||
public updateAim(normalizeCharacterMoveSpeed:number, toMax = false) {
|
||||
|
||||
if (this.currentEquipItem === undefined) {
|
||||
if (this.stableValue !== 0){
|
||||
this.stableValue = 0;
|
||||
if(this._actor.isPlayer) Msg.emit('msg_update_aim', this.stableValue);
|
||||
}
|
||||
}else{
|
||||
const equipData = this.currentEquipItem.data;
|
||||
const equipStable = equipData.stable_max_value;
|
||||
let currentStable = 0;
|
||||
if(toMax) {
|
||||
this.stableValue = equipData.stable_max_value;
|
||||
currentStable = equipData.stable_max_value;
|
||||
}else{
|
||||
if (equipStable !== 0) {
|
||||
currentStable = Math.abs(normalizeCharacterMoveSpeed) <= 0.001 ? equipData.stable_min_value : equipData.stable_max_value * normalizeCharacterMoveSpeed;
|
||||
currentStable = Math.max(equipData.stable_min_value, currentStable);
|
||||
}
|
||||
this.stableValue = math.lerp(this.stableValue, currentStable, game.deltaTime * equipData.stable_smooth);
|
||||
}
|
||||
|
||||
if(this._actor.isPlayer) Msg.emit('msg_update_aim', this.stableValue);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "74f9852c-4476-42f6-9993-3d25969fbcbf",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
import { _decorator, Component, math, Node, v3, CCFloat } from 'cc';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
import { ActorMove } from '../actor/actor-move';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorFace')
|
||||
export class ActorFace extends Component {
|
||||
|
||||
@property(Node)
|
||||
rotationNode: Node | undefined;
|
||||
|
||||
@property({ type: ActorMove, tooltip: 'Test actor move.' })
|
||||
actorMove: ActorMove | undefined;
|
||||
|
||||
@property(CCFloat)
|
||||
smoothAngle = 20;
|
||||
|
||||
@property(CCFloat)
|
||||
smoothHeight = 1;
|
||||
|
||||
targetAngle = v3(0, 0, 0);
|
||||
currentAngle = v3(0, 0, 0);
|
||||
|
||||
targetPosition = v3(0, 0, 0);
|
||||
currentPosition = v3(0, 0, 0);
|
||||
|
||||
start () {
|
||||
UtilVec3.copy(this.targetAngle, this.rotationNode!.eulerAngles);
|
||||
UtilVec3.copy(this.currentAngle, this.targetAngle);
|
||||
UtilVec3.copy(this.targetPosition, this.rotationNode!.position);
|
||||
UtilVec3.copy(this.currentAngle, this.targetPosition);
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
|
||||
this.rotationX(this.actorMove!.angleVertical);
|
||||
|
||||
this.currentAngle.x = math.lerp(this.currentAngle.x, this.targetAngle.x, this.smoothAngle * deltaTime);
|
||||
|
||||
this.rotationNode?.setRotationFromEuler(this.currentAngle);
|
||||
|
||||
this.currentPosition.y = math.lerp(this.currentPosition.y, this.targetPosition.y, this.smoothHeight * deltaTime);
|
||||
|
||||
this.rotationNode?.setPosition(this.currentPosition);
|
||||
|
||||
}
|
||||
|
||||
rotationX (angleX: number) {
|
||||
|
||||
this.targetAngle.x = angleX;
|
||||
|
||||
}
|
||||
|
||||
setRootY (height: number) {
|
||||
this.targetPosition.y = height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d7147c1b-98b1-4c7b-bba6-347728d2cc39",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { _decorator, Component } from 'cc';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('ActorFollowPathMove')
|
||||
export class ActorFollowPathMove extends Component {
|
||||
|
||||
//public setMove()
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "11a177b8-e3f6-4cf2-99a1-efe4cf8f86e3",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
import { _decorator, v3 } from 'cc';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { Res } from '../../core/res/res';
|
||||
import { ResCache } from '../../core/res/res-cache';
|
||||
import { ProjectileGrenade } from './projectile-grenade';
|
||||
import { Level } from '../level/level';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorGrenade')
|
||||
export class ActorGrenade extends ActorEquipBase {
|
||||
|
||||
onFire() {
|
||||
const forwardNode = this._actor!._forwardNode!;
|
||||
const origin = forwardNode.worldPosition;
|
||||
const dir = forwardNode.forward;
|
||||
const prefab = ResCache.Instance.getPrefab(this._data.projectile_res);
|
||||
let position = v3(origin.x, origin.y, origin.z);
|
||||
position.add(dir);
|
||||
const projectile = Res.instNode(prefab, Level.Instance._objectNode, position);
|
||||
const projectileGrenade = projectile.getComponent(ProjectileGrenade);
|
||||
const throwDir = dir.multiplyScalar(10);
|
||||
projectileGrenade?.onThrow(this._data, throwDir, this._actor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "aeef2555-a4d2-4b63-9ca5-9e5eb71b4a34",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { _decorator, Component, Node, geometry, PhysicsSystem, game, PhysicsRayResult, Vec3, director } from 'cc';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { calculateDamage } from './damage-core';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
let ray = new geometry.Ray();
|
||||
|
||||
@ccclass('ActorHandgun')
|
||||
export class ActorHandgun extends ActorEquipBase {
|
||||
|
||||
onFire() {
|
||||
this._bagData!.bulletCount--;
|
||||
const forwardNode = this._actor!._forwardNode!;
|
||||
const origin = forwardNode.worldPosition;
|
||||
const dir = forwardNode.forward;
|
||||
UtilVec3.copy(ray.o, origin);
|
||||
UtilVec3.copy(ray.d, dir);
|
||||
const distance = this._data.damage.distance;
|
||||
let hit:PhysicsRayResult | undefined;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, this.mask, distance)) {
|
||||
hit = PhysicsSystem.instance.raycastClosestResult;
|
||||
}
|
||||
this.showTracer(hit, dir);
|
||||
calculateDamage(this._data, hit, this._actor);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "84405be6-9660-4ef3-8904-d5500e1dbbfa",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
|
||||
import { _decorator, Component, find, Vec2, PhysicsSystem, input, Input, EventMouse, geometry, Camera, game, EventTouch, director, Vec3 } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
import { IActorInput } from '../../core/input/IActorInput';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { Actor } from './actor';
|
||||
|
||||
|
||||
@ccclass('ActorInputBrain')
|
||||
export class ActorInputBrain extends Component implements IActorInput {
|
||||
|
||||
_actor:IActorInput | undefined | null;
|
||||
|
||||
_isPause = false;
|
||||
|
||||
start () {
|
||||
this._actor = this.getComponent(Actor);
|
||||
if (this._actor === null) {
|
||||
throw new Error(`${this.node.name} node can not find ActorEnemy`);
|
||||
}
|
||||
}
|
||||
|
||||
onMove(move:Vec3) {
|
||||
this._actor?.onMove(move);
|
||||
}
|
||||
|
||||
onRotation(x:number, y:number){
|
||||
this._actor?.onRotation(x, y);
|
||||
}
|
||||
|
||||
onDir(x:number, y:number) {
|
||||
this._actor?.onDir(x, y);
|
||||
}
|
||||
|
||||
onJump() {
|
||||
this._actor?.onJump();
|
||||
}
|
||||
|
||||
onRun(isRun:boolean) {
|
||||
this._actor?.onRun(isRun);
|
||||
}
|
||||
|
||||
onCrouch() {
|
||||
this._actor?.onCrouch();
|
||||
}
|
||||
|
||||
onProne(){
|
||||
//this._actor?.onProne();
|
||||
}
|
||||
|
||||
onAim(isAim:boolean | undefined): void {
|
||||
this._actor?.onAim(isAim);
|
||||
}
|
||||
onChangeEquips(): boolean {
|
||||
throw new Error('Method not implemented.');
|
||||
}
|
||||
|
||||
|
||||
onFire() {
|
||||
this._actor?.onFire();
|
||||
}
|
||||
|
||||
onEquip(index:number) {
|
||||
this._actor?.onEquip(index);
|
||||
}
|
||||
|
||||
onPick() {
|
||||
this._actor?.onPick();
|
||||
}
|
||||
|
||||
onReload() {
|
||||
this._actor?.onReload();
|
||||
}
|
||||
|
||||
onDrop() {
|
||||
this._actor?.onDrop();
|
||||
}
|
||||
|
||||
onPause() {}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "9a779e15-18f0-460e-97f8-ee362f501aa8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
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, Vec3, sys } from 'cc';
|
||||
const { ccclass } = _decorator;
|
||||
import { IActorInput } from '../../core/input/IActorInput';
|
||||
import { Level } from '../level/level';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { UI } from '../../core/ui/ui';
|
||||
|
||||
@ccclass('ActorInput')
|
||||
export class ActorInput extends Component implements IActorInput {
|
||||
|
||||
_actor:IActorInput | undefined | null;
|
||||
_isPause = false;
|
||||
|
||||
_isOpenEquips = false;
|
||||
|
||||
public static inst:ActorInput | undefined;
|
||||
|
||||
start () {
|
||||
Msg.on('msg_set_input_active', this.setActive.bind(this));
|
||||
ActorInput.inst = this;
|
||||
|
||||
Msg.on('msg_exit_pointer',this.exitPointer.bind(this));
|
||||
}
|
||||
|
||||
exitPointer() {
|
||||
document.exitPointerLock();
|
||||
}
|
||||
|
||||
onDestroy() {
|
||||
Msg.off('msg_set_input_active', this.setActive.bind(this));
|
||||
Msg.off('msg_exit_pointer', this.exitPointer.bind(this));
|
||||
|
||||
if(sys.platform === sys.Platform.MOBILE_BROWSER ||
|
||||
sys.platform === sys.Platform.ANDROID ||
|
||||
sys.platform === sys.Platform.IOS ) {
|
||||
UI.Instance.off('ui_joystick');
|
||||
}
|
||||
}
|
||||
|
||||
setActive(isShow:boolean) {
|
||||
if(isShow) {
|
||||
this.initInput();
|
||||
}else{
|
||||
for(let i = 0; i < this.node.children.length; i++) this.node.children[i].active = false;
|
||||
UI.Instance.off('ui_joystick');
|
||||
}
|
||||
}
|
||||
|
||||
initInput() {
|
||||
|
||||
this._actor = Level.Instance._player;
|
||||
|
||||
// Select the type of input device enabled based on the platform.
|
||||
if(sys.platform === sys.Platform.MOBILE_BROWSER ||
|
||||
sys.platform === sys.Platform.ANDROID ||
|
||||
sys.platform === sys.Platform.IOS ) {
|
||||
UI.Instance.on('ui_joystick');
|
||||
}else {
|
||||
this.node.children[1].active = true;
|
||||
this.node.children[0].active = true;
|
||||
//UI.Instance.on('ui_joystick');
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
onMove(move:Vec3) {
|
||||
this._actor?.onMove(move);
|
||||
}
|
||||
|
||||
onRotation(x:number, y:number){
|
||||
this._actor?.onRotation(x, y);
|
||||
}
|
||||
|
||||
onJump() {
|
||||
this._actor?.onJump();
|
||||
}
|
||||
|
||||
onRun(isRun:boolean) {
|
||||
this._actor?.onRun(isRun);
|
||||
}
|
||||
|
||||
onCrouch() {
|
||||
this._actor?.onCrouch();
|
||||
}
|
||||
|
||||
onAim(){
|
||||
this._actor?.onAim(undefined);
|
||||
}
|
||||
|
||||
onFire() {
|
||||
this._actor?.onFire();
|
||||
}
|
||||
|
||||
onAutoFire(isAutoFire:boolean) {
|
||||
this._actor?.onAutoFire(isAutoFire);
|
||||
}
|
||||
|
||||
onEquip(index:number) {
|
||||
this._actor?.onEquip(index);
|
||||
}
|
||||
|
||||
onPick() {
|
||||
this._actor?.onPick();
|
||||
}
|
||||
|
||||
onReload() {
|
||||
this._actor?.onReload();
|
||||
}
|
||||
|
||||
onDrop() {
|
||||
this._actor?.onDrop();
|
||||
}
|
||||
|
||||
onDir(x: number, y: number) {
|
||||
}
|
||||
|
||||
onPause() {
|
||||
|
||||
this._isPause = !this._isPause;
|
||||
Msg.emit('push', 'level_pause');
|
||||
|
||||
}
|
||||
|
||||
onChangeEquips() {
|
||||
Msg.emit('push', 'select_equips');
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "01c0931b-1410-4e31-a555-3d1093b99c13",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
export interface IActorEquip {
|
||||
|
||||
onUse();
|
||||
onDrop();
|
||||
onPick();
|
||||
|
||||
}
|
||||
|
||||
export class DamageData {
|
||||
hitPart:string | undefined;
|
||||
hitDistance:number | undefined;
|
||||
fireData:any;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "92104162-688d-4e2e-bb59-9b4be96a170b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { Component, _decorator } from "cc";
|
||||
import { BagItem } from "./actor-bag";
|
||||
import { IActorEquip } from "./actor-interface";
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('ActorItem')
|
||||
export class ActorItem extends Component {
|
||||
|
||||
data:BagItem | undefined;
|
||||
item:IActorEquip | undefined;
|
||||
|
||||
start() {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "bccb4286-0a47-4ba7-83f0-724984017f11",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { _decorator, Component, Node, find } from 'cc';
|
||||
import { UtilNode } from '../../core/util/util';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorLookAt')
|
||||
export class ActorLookAt extends Component {
|
||||
|
||||
@property
|
||||
bone_name = 'bone_root';
|
||||
|
||||
@property(Node)
|
||||
bone_head: Node = Object.create(null);
|
||||
|
||||
_angle = 0;
|
||||
_dir = 1;
|
||||
|
||||
start () {
|
||||
this.bone_head = UtilNode.find(this.node, this.bone_name);
|
||||
}
|
||||
|
||||
lateUpdate (deltaTime: number) {
|
||||
|
||||
this.bone_head?.setRotationFromEuler(this._angle, 0, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "9a855bfe-32b2-4644-99e0-73aef065a33d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { _decorator, geometry, PhysicsSystem, PhysicsRayResult } from 'cc';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { calculateDamage } from './damage-core';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
let ray = new geometry.Ray();
|
||||
|
||||
@ccclass('ActorMachineGun')
|
||||
export class ActorMachineGun extends ActorEquipBase {
|
||||
|
||||
onFire() {
|
||||
this._bagData!.bulletCount--;
|
||||
const forwardNode = this._actor!._forwardNode!;
|
||||
const origin = forwardNode.worldPosition;
|
||||
const dir = forwardNode.forward;
|
||||
UtilVec3.copy(ray.o, origin);
|
||||
UtilVec3.copy(ray.d, dir);
|
||||
const distance = this._data.damage.distance;
|
||||
let hit:PhysicsRayResult | undefined;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, this.mask, distance)) {
|
||||
hit = PhysicsSystem.instance.raycastClosestResult;
|
||||
}
|
||||
this.showTracer(hit, dir);
|
||||
calculateDamage(this._data, hit, this._actor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d1de3d77-deed-4506-a59b-69a8b0abf0f2",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorMain')
|
||||
export class ActorMain extends Component {
|
||||
|
||||
public static target = Object.create(null);
|
||||
|
||||
start () {
|
||||
ActorMain.target = this.node;
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "fa586b1e-eba7-4914-bfcc-44d35bbf264e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
import { _decorator, geometry, PhysicsSystem, PhysicsRayResult } from 'cc';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { calculateDamage } from './damage-core';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorMeleeWeapon')
|
||||
export class ActorMeleeWeapon extends ActorEquipBase {
|
||||
|
||||
onFire() {
|
||||
this._bagData!.bulletCount--;
|
||||
const forwardNode = this._actor!._forwardNode!;
|
||||
const origin = forwardNode.worldPosition;
|
||||
const dir = forwardNode.forward;
|
||||
let ray = new geometry.Ray(origin.x, origin.y, origin.z, dir.x, dir.y , dir.z);
|
||||
const mask = 1 << 3 | 1 << 4;
|
||||
const distance = this._data.damage.distance;
|
||||
let hit:PhysicsRayResult | undefined = undefined;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, mask, distance)) {
|
||||
hit = PhysicsSystem.instance.raycastClosestResult;
|
||||
}
|
||||
calculateDamage(this._data, hit, this._actor);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "4bb28e3d-fc72-42ea-b3bb-b52241fa1d05",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
import { _decorator, Component, geometry, Node, PhysicsRayResult, PhysicsSystem, v3, Vec3 } from 'cc';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorMoveSlope')
|
||||
export class ActorMoveSlope extends Component {
|
||||
|
||||
ray = new geometry.Ray();
|
||||
mask = 0;
|
||||
|
||||
distance = 0.4
|
||||
|
||||
p0 = v3(0, 0, 0);
|
||||
p1 = v3(0, 0, 0);
|
||||
|
||||
direction = v3(0, 0, 0);
|
||||
|
||||
start() {
|
||||
this.mask = 1 << 3 | 1 << 4;
|
||||
UtilVec3.copy(this.ray.d, Vec3.ZERO);
|
||||
this.ray.d = v3(0, -1, 0);
|
||||
this.distance = 0.3;
|
||||
}
|
||||
|
||||
updateSlope(moveDirection:Vec3):Vec3 {
|
||||
|
||||
const moveLength = moveDirection.length();
|
||||
|
||||
if(moveLength === 0) return Vec3.ZERO;
|
||||
|
||||
UtilVec3.copy(this.ray.o, this.node.worldPosition);
|
||||
UtilVec3.copy(this.direction, moveDirection);
|
||||
|
||||
if (PhysicsSystem.instance.raycastClosest(this.ray, this.mask, this.distance)) {
|
||||
const hit1 = PhysicsSystem.instance.raycastClosestResult;
|
||||
UtilVec3.copy(this.p0, hit1.hitPoint);
|
||||
this.ray.o.add(moveDirection.normalize().multiplyScalar(0.03));
|
||||
if (PhysicsSystem.instance.raycastClosest(this.ray, this.mask, this.distance)) {
|
||||
const hit2 = PhysicsSystem.instance.raycastClosestResult;
|
||||
UtilVec3.copy(this.direction, hit2.hitPoint);
|
||||
this.direction.subtract(this.p0).normalize().multiplyScalar(moveLength);
|
||||
}
|
||||
}
|
||||
|
||||
return this.direction;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "ab95dc11-75d1-4161-a9f8-763a19f2421d",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import { _decorator, Component, math, Node, RigidBody, v3, Vec3, CCFloat } from 'cc';
|
||||
import { ActorMoveSlope } from './actor-move-slope';
|
||||
import { UtilVec3 } from '../../core/util/util';
|
||||
import { SensorSlope } from '../../core/sensor/sensor-slope';
|
||||
import { SensorGround } from '../../core/sensor/sensor-ground';
|
||||
import { fun } from '../../core/util/fun';
|
||||
import { Level } from '../level/level';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
let tempRotationSideVector = v3(0, 0, 0);
|
||||
|
||||
@ccclass('ActorMove')
|
||||
export class ActorMove extends Component {
|
||||
|
||||
@property({ type: CCFloat, tooltip: 'Move Speed. ' })
|
||||
speed = 1;
|
||||
|
||||
@property({ tooltip: 'Jump Force.' })
|
||||
jumpForce = v3(0, 6.0, 0);
|
||||
|
||||
@property({ type: CCFloat, tooltip: 'Move smooth value.' })
|
||||
smoothMove = 5;
|
||||
|
||||
@property({ type: CCFloat, tooltip: 'Default angle value' })
|
||||
angleVertical = 0;
|
||||
|
||||
@property({ type: SensorSlope, tooltip: ' Sensor slope.' })
|
||||
sensorSlop: SensorSlope | undefined;
|
||||
|
||||
@property({ type: SensorGround, tooltip: ' Sensor ground.' })
|
||||
sensorGround: SensorGround | undefined;
|
||||
|
||||
velocity = v3(0, 0, 0);
|
||||
velocityLocal = v3(0, 0, 0);
|
||||
currentVelocity: Vec3 = v3(0, 0, 0);
|
||||
moveVec3 = new Vec3(0, 0, 0);
|
||||
|
||||
currentDirection = v3(0, 0, 0);
|
||||
direction = v3(0, 0, 0);
|
||||
angleHead = 0;
|
||||
|
||||
rigid: RigidBody | undefined;
|
||||
|
||||
@property
|
||||
angleVerticalMax = 30;
|
||||
|
||||
@property
|
||||
angleVerticalMin = -30;
|
||||
|
||||
@property
|
||||
faceMove = true;
|
||||
|
||||
angle = 0;
|
||||
|
||||
isJump = false;
|
||||
|
||||
isStopMove = false;
|
||||
|
||||
start () {
|
||||
|
||||
this.rigid = this.getComponent(RigidBody)!;
|
||||
this.sensorSlop = this.getComponent(SensorSlope)!;
|
||||
this.sensorGround = this.getComponent(SensorGround)!;
|
||||
|
||||
this.node.setRotationFromEuler(0, 180, 0);
|
||||
this.onRotation(180, 0);
|
||||
}
|
||||
|
||||
lateUpdate (deltaTime: number) {
|
||||
if (Level.Instance.stop) {
|
||||
this.stop();
|
||||
return;
|
||||
}
|
||||
if (this.isStopMove) return;
|
||||
this.movePosition(deltaTime);
|
||||
this.moveRotation();
|
||||
}
|
||||
|
||||
movePosition (deltaTime: number) {
|
||||
|
||||
//Lerp velocity.
|
||||
Vec3.lerp(this.velocityLocal, this.velocityLocal, this.moveVec3, deltaTime * this.smoothMove);
|
||||
UtilVec3.copy(this.velocity, this.velocityLocal);
|
||||
|
||||
//rotate y.
|
||||
if (this.faceMove)
|
||||
Vec3.rotateY(this.velocity, this.velocity, Vec3.ZERO, math.toRadian(this.node.eulerAngles.y));
|
||||
|
||||
this.rigid?.getLinearVelocity(this.currentVelocity);
|
||||
this.velocity.y = this.currentVelocity.y;
|
||||
|
||||
if (this.sensorGround?._isGround && this.sensorSlop!.checkSlope(this.velocity)) {
|
||||
const moveLength = this.velocity.length();
|
||||
UtilVec3.copy(this.velocity, this.sensorSlop!.vectorSlop);
|
||||
this.velocity.normalize().multiplyScalar(moveLength);
|
||||
}
|
||||
|
||||
this.rigid?.setLinearVelocity(this.velocity);
|
||||
}
|
||||
|
||||
moveRotation () {
|
||||
UtilVec3.copy(this.currentDirection, this.direction);
|
||||
this.angle = Math.abs(Vec3.angle(this.currentDirection, this.node.forward));
|
||||
if (this.angle > 0.001) {
|
||||
UtilVec3.copy(tempRotationSideVector, this.currentDirection);
|
||||
const side = Math.sign(-tempRotationSideVector.cross(this.node.forward).y);
|
||||
const angle = side * this.angle * 20 + this.node.eulerAngles.y;
|
||||
this.node.setRotationFromEuler(0, angle, 0);
|
||||
}
|
||||
}
|
||||
|
||||
moveDirection (direction: Vec3) {
|
||||
UtilVec3.copy(this.moveVec3, direction);
|
||||
this.moveVec3.multiplyScalar(this.speed);
|
||||
}
|
||||
|
||||
jump () {
|
||||
//this.rigid?.applyImpulse(this.jumpForce);
|
||||
this.rigid?.getLinearVelocity(this.currentVelocity);
|
||||
this.currentVelocity.y = 7;
|
||||
this.rigid?.setLinearVelocity(this.currentVelocity);
|
||||
}
|
||||
|
||||
onRotation (x: number, y: number) {
|
||||
this.angleHead += x;
|
||||
this.direction.z = -Math.cos(Math.PI / 180.0 * this.angleHead);
|
||||
this.direction.x = Math.sin(Math.PI / 180.0 * this.angleHead);
|
||||
this.angleVertical -= y;
|
||||
if (this.angleVertical >= this.angleVerticalMax)
|
||||
this.angleVertical = this.angleVerticalMax;
|
||||
|
||||
if (this.angleVertical <= this.angleVerticalMin)
|
||||
this.angleVertical = this.angleVerticalMin;
|
||||
}
|
||||
|
||||
onDirection (x: number, y: number, z: number) {
|
||||
|
||||
this.direction.x = x;
|
||||
this.direction.z = z;
|
||||
|
||||
this.angleVertical = y;
|
||||
if (this.angleVertical >= this.angleVerticalMax)
|
||||
this.angleVertical = this.angleVerticalMax;
|
||||
|
||||
if (this.angleVertical <= this.angleVerticalMin)
|
||||
this.angleVertical = this.angleVerticalMin;
|
||||
|
||||
}
|
||||
|
||||
stop () {
|
||||
this.rigid!.getLinearVelocity(this.velocity);
|
||||
this.velocity.x = 0;
|
||||
this.velocity.z = 0;
|
||||
this.velocity.y = 0;
|
||||
this.rigid!.setLinearVelocity(this.velocity);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b31a5903-4723-43f3-8149-9e8ccfe4b8e8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import { ActorBase } from '../../core/actor/actor-base';
|
||||
import { UtilNode } from '../../core/util/util';
|
||||
import { Actor } from './actor';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorPart')
|
||||
export class ActorPart extends Component {
|
||||
|
||||
@property( { type:ActorBase } )
|
||||
actor:ActorBase | undefined;
|
||||
|
||||
@property
|
||||
part = 'body';
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "f8ec2ae9-27d0-455d-8cc0-985302d24894",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
import { _decorator, Component, Vec3, v3, v2, random, RigidBody, inverseLerp } from 'cc';
|
||||
import { SensorRaysAngle } from '../../core/sensor/sensor-rays-angle';
|
||||
import { UtilNode } from '../../core/util/util';
|
||||
|
||||
const { ccclass } = _decorator;
|
||||
|
||||
@ccclass('ActorPhysicalSkin')
|
||||
export class ActorPhysicalSkin extends Component {
|
||||
|
||||
sensor:SensorRaysAngle | undefined;
|
||||
rigid:RigidBody | undefined;
|
||||
velocity:Vec3 = v3(0, 0, 0);
|
||||
dir:Vec3 = v3(0, 0, 0);
|
||||
velocityPlane:Vec3 = v3(0, 0, 0);
|
||||
inverseForce:Vec3 = v3(0, 0, 0);
|
||||
|
||||
start() {
|
||||
this.sensor = UtilNode.getChildComponent(this.node, 'skin', SensorRaysAngle); //this.node.getChildByName('skin').getComponent(SensorRaysAngle);
|
||||
this.rigid = UtilNode.getComponent(this.node, RigidBody); //this.getComponent(RigidBody);
|
||||
this.velocity = v3(0, 0, 0);
|
||||
}
|
||||
|
||||
lateUpdate(deltaTime:number) {
|
||||
/*
|
||||
if (this.sensor!.checked) {
|
||||
this.rigid!.getLinearVelocity(this.velocity);
|
||||
// change move direction.
|
||||
const position = this.node.worldPosition;
|
||||
this.dir.x = this.sensor!.hitPoint.x - position.x;
|
||||
this.dir.z = this.sensor!.hitPoint.z - position.z;
|
||||
|
||||
this.velocityPlane.x = this.velocity.x;
|
||||
this.velocityPlane.z = this.velocity.z;
|
||||
|
||||
if (Vec3.angle(this.dir, this.velocityPlane) < 10) {
|
||||
//this.velocity.x = 0;
|
||||
//this.velocity.z = 0;
|
||||
this.velocity = this.velocity.normalize().multiplyScalar(3);
|
||||
this.rigid!.setLinearVelocity(this.velocity);
|
||||
// add inverse force.
|
||||
//this.inverseForce.x = -this.velocity.x;
|
||||
//this.inverseForce.z = -this.velocity.z;
|
||||
//this.rigid.applyImpulse(this.inverseForce.normalize().multiplyScalar(1));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "cc90bda3-0f0d-45cb-9307-f9d1ffd14b3e",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import { ActorBase } from '../../core/actor/actor-base';
|
||||
import { Brain } from '../../core/ai/brain';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorPiranha')
|
||||
export class ActorPiranha extends ActorBase {
|
||||
|
||||
_brain:Brain;
|
||||
|
||||
start() {
|
||||
this.init('actor-piranha');
|
||||
this._brain = new Brain(this._data.brain, this);
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
}
|
||||
|
||||
//#region condition
|
||||
|
||||
noFoundPlayer() {
|
||||
|
||||
}
|
||||
|
||||
foundPlayer() {
|
||||
|
||||
}
|
||||
|
||||
nearPlayer() {
|
||||
|
||||
}
|
||||
|
||||
canEatPlayer() {
|
||||
|
||||
}
|
||||
|
||||
fleePlayer() {
|
||||
|
||||
}
|
||||
|
||||
feedPlayer() {
|
||||
|
||||
}
|
||||
|
||||
//#endregion
|
||||
|
||||
idle() {
|
||||
|
||||
}
|
||||
|
||||
move() {
|
||||
|
||||
}
|
||||
|
||||
jump() {
|
||||
|
||||
}
|
||||
|
||||
crossRoad() {
|
||||
|
||||
}
|
||||
|
||||
forcePrepareJump() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "88d40cc5-929d-4d41-b6e0-ac722abaceb8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { _decorator, Component, Node } from 'cc';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { SensorRays } from '../../core/sensor/sensor-rays';
|
||||
import { Actor } from './actor';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorSensorDropItem')
|
||||
export class ActorSensorDropItem extends Component {
|
||||
|
||||
//@property(Actor)
|
||||
//actor:Actor | undefined | null;
|
||||
|
||||
@property
|
||||
num = 3;
|
||||
|
||||
sensor: SensorRays | undefined | null;
|
||||
pickedNode: Node | undefined
|
||||
|
||||
state = -1;
|
||||
curState = -1;
|
||||
|
||||
start () {
|
||||
this.sensor = this.getComponent(SensorRays);
|
||||
if (this.sensor === null) {
|
||||
throw new Error(`${this.node.name} node can not find 'SensorRays' component.`);
|
||||
}
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
|
||||
if (this.sensor!.checked) {
|
||||
this.pickedNode = this.sensor!.checkedNode;
|
||||
const dropName = this.pickedNode!.name
|
||||
this.curState = 255;
|
||||
console.log('check drop name:', dropName);
|
||||
} else {
|
||||
this.curState = 0;
|
||||
this.pickedNode = undefined;
|
||||
}
|
||||
|
||||
if (this.state !== this.curState) {
|
||||
this.state = this.curState;
|
||||
Msg.emit('msg_grp_take_info', this.state);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public getPicked () {
|
||||
|
||||
if (this.pickedNode != undefined) {
|
||||
return this.pickedNode;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d350878e-4198-4b48-99b7-c28950ab57a7",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { _decorator, Component, Node, geometry, PhysicsSystem, game } from 'cc';
|
||||
import { ActorBase } from '../../core/actor/actor-base';
|
||||
import { ActorEquipBase } from './actor-equip-base';
|
||||
import { ActorPart } from './actor-part';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorShotgun')
|
||||
export class ActorShotgun extends ActorEquipBase {
|
||||
|
||||
onFire() {
|
||||
this._bagData.bulletCount--;
|
||||
const forwardNode = this._actor._forwardNode;
|
||||
const origin = forwardNode.worldPosition;
|
||||
const dir = forwardNode.forward;
|
||||
let ray = new geometry.Ray(origin.x, origin.y, origin.z, dir.x, dir.y , dir.z);
|
||||
const mask = 1 << 3;
|
||||
const distance = this._data.damage.distance;
|
||||
if (PhysicsSystem.instance.raycastClosest(ray, mask, distance)) {
|
||||
const res = PhysicsSystem.instance.raycastClosestResult;
|
||||
const hitName = res.collider.node.name;
|
||||
console.log(`handgun fire hit ${hitName}`);
|
||||
if (hitName.concat('actor')) {
|
||||
const actorPart = res.collider.node.getComponent(ActorPart);
|
||||
if (!actorPart) {
|
||||
console.error(` damage part can not add actor part component. ${actorPart}`);
|
||||
}
|
||||
const actor = actorPart.actor;
|
||||
const damage = this._data.damage[hitName];
|
||||
if (damage === undefined) {
|
||||
console.error(`hit part undefind ${hitName}`);
|
||||
}
|
||||
actor._data.hp -= damage;
|
||||
if (actor._data.hp <= 0) {
|
||||
this._actor._data.hp = 1;
|
||||
actor.do('dead');
|
||||
}
|
||||
}else if (hitName === 'col_brick') {
|
||||
|
||||
}else if (hitName === 'col_metal') {
|
||||
|
||||
}else{
|
||||
|
||||
}
|
||||
|
||||
}else{
|
||||
console.log('empty shoot.');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "1f1b9b99-4829-479d-8974-688695b69e88",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { _decorator, Component, randomRangeInt } from 'cc';
|
||||
import { Sound } from '../../core/audio/sound';
|
||||
import { KeyAnyType } from '../data/game-type';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { Actor } from './actor';
|
||||
import { DataSoundInst } from '../data/data-core';
|
||||
import { Level } from '../level/level';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorSound')
|
||||
export class ActorSound extends Component {
|
||||
|
||||
@property
|
||||
stepLength = 1.2;
|
||||
|
||||
_currentStepLength = 0;
|
||||
|
||||
_data:KeyAnyType = {};
|
||||
|
||||
actor: Actor | undefined;
|
||||
|
||||
start() {
|
||||
this.actor = this.getComponent(Actor)!;
|
||||
this._data = this.actor._data;
|
||||
Msg.on('msg_walk_sfx', this.walkSfx.bind(this));
|
||||
}
|
||||
|
||||
onDestroy () {
|
||||
Msg.off('msg_walk_sfx', this.walkSfx.bind(this));
|
||||
}
|
||||
|
||||
update(deltaTime:number) {
|
||||
|
||||
// If Level is stop return.
|
||||
if(Level.Instance.stop) return;
|
||||
|
||||
if(this._data.is_ground)
|
||||
this._currentStepLength += Math.abs(deltaTime * this.actor!._actorMove!.velocityLocal?.length());
|
||||
|
||||
if(this._currentStepLength >= this.stepLength) {
|
||||
this.walkSfx();
|
||||
this._currentStepLength -= this.stepLength;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
walkSfx () {
|
||||
|
||||
const type = `walk_${this._data.walk_in_type}`;
|
||||
const soundList = DataSoundInst.get(type);
|
||||
const index = randomRangeInt(0, soundList.length);
|
||||
Sound.on(soundList[index]);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "f7156bcd-3448-4ccf-a5f0-7c2b57694ba9",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { _decorator, Component, Node, v3, Vec3 } from 'cc';
|
||||
import { Actor } from './actor';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ActorStatistics')
|
||||
export class ActorStatistics extends Component {
|
||||
|
||||
actor:Actor | undefined;
|
||||
|
||||
_velocity = v3(0, 0, 0);
|
||||
|
||||
_statisticsTime = 1;
|
||||
|
||||
_moveDistance = 0;
|
||||
|
||||
_runDistance = 0;
|
||||
|
||||
start() {
|
||||
this.actor = this.getComponent(Actor)!;
|
||||
}
|
||||
|
||||
update(deltaTime: number) {
|
||||
|
||||
this.actor?._actorMove?.rigid?.getLinearVelocity(this._velocity);
|
||||
|
||||
this._velocity.y = 0;
|
||||
|
||||
const length = this._velocity.length();
|
||||
if(length > 0.1) {
|
||||
const distance = length * deltaTime;
|
||||
|
||||
this._moveDistance += distance;
|
||||
|
||||
if(this.actor?._data.isRun) {
|
||||
this._runDistance += distance;
|
||||
}
|
||||
|
||||
this._statisticsTime -= deltaTime;
|
||||
if(this._statisticsTime <= 0) {
|
||||
this._statisticsTime = 1;
|
||||
Msg.emit('msg_stat_distance', {key:'move', distance:this._moveDistance});
|
||||
|
||||
if(this._runDistance > 0)
|
||||
Msg.emit('msg_stat_distance', {key:'run', distance:this._runDistance});
|
||||
|
||||
this._moveDistance = 0;
|
||||
this._runDistance = 0;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "6dcab13a-96ff-4fad-ab1f-b14ef40a5667",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,383 @@
|
||||
/*
|
||||
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, Vec3, v3, game, Node, math } from 'cc';
|
||||
import { ActorBase } from '../../core/actor/actor-base';
|
||||
import { IActorInput } from '../../core/input/IActorInput';
|
||||
import { Local } from '../../core/localization/local';
|
||||
import { Msg } from '../../core/msg/msg';
|
||||
import { UtilNode, UtilVec3 } from '../../core/util/util';
|
||||
import { ActorAnimationGraph } from './actor-animation-graph';
|
||||
import { ActorBag } from './actor-bag';
|
||||
import { ActorEquipment } from './actor-equipment';
|
||||
import { ActorSensorDropItem } from './actor-sensor-drop-item';
|
||||
import { ActorMove } from './actor-move';
|
||||
import { SensorGround } from '../../core/sensor/sensor-ground';
|
||||
import { ActorFace } from './actor-face';
|
||||
import { Sound } from '../../core/audio/sound';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
let tempLinearVelocity = v3(0, 0, 0);
|
||||
let tempAngleVelocity = v3(0, 0, 0);
|
||||
|
||||
@ccclass('Actor')
|
||||
export class Actor extends ActorBase implements IActorInput {
|
||||
|
||||
_move = v3(0, 0, 0);
|
||||
_actorBag: ActorBag | undefined;
|
||||
_actorEquipment: ActorEquipment | undefined;
|
||||
|
||||
@property({ type: ActorSensorDropItem })
|
||||
actorSensorDropItem: ActorSensorDropItem | undefined;
|
||||
|
||||
@property({ type: ActorFace })
|
||||
_actorFace: ActorFace | undefined;
|
||||
|
||||
_actorSensorGround: SensorGround | undefined;
|
||||
_actorMove: ActorMove | undefined;
|
||||
_viewNoWeapon: Node = Object.create(null);
|
||||
_forwardNode: Node | undefined;
|
||||
_viewRoot: Node | undefined;
|
||||
|
||||
// forward
|
||||
forward: Vec3 = v3(0, 0, 0);
|
||||
_fps = 0;
|
||||
isReady = false;
|
||||
|
||||
bulletBox = 2;
|
||||
|
||||
get noAction () {
|
||||
return this._data.is_dead || this._data.is_win;
|
||||
}
|
||||
|
||||
initView () {
|
||||
|
||||
super.initView();
|
||||
|
||||
this._actorBag = new ActorBag(this);
|
||||
this._actorEquipment = new ActorEquipment(this);
|
||||
this._actorSensorGround = this.node.getComponent(SensorGround)!;
|
||||
this._actorMove = this.getComponent(ActorMove)!;
|
||||
this._forwardNode = UtilNode.find(this.node, 'forwardNode');
|
||||
this._viewRoot = UtilNode.find(this.node, 'animation_view');
|
||||
this._animationGraph = this._viewRoot.getComponent(ActorAnimationGraph)!;
|
||||
|
||||
this.do('play');
|
||||
}
|
||||
|
||||
onUpdate () {
|
||||
super.onUpdate();
|
||||
this._updates.push(this.updateAction.bind(this));
|
||||
}
|
||||
|
||||
do (name: string) {
|
||||
if (this.noAction) return;
|
||||
super.do(name);
|
||||
}
|
||||
|
||||
updateAction (deltaTime: number) {
|
||||
|
||||
this._fps = game.frameRate as number;
|
||||
|
||||
if (this._data.hit_recover > 0) {
|
||||
this._data.hit_recover -= deltaTime;
|
||||
this._actorMove!.isStopMove = true;
|
||||
this._actorMove?.stop();
|
||||
} else {
|
||||
this._actorMove!.isStopMove = false;
|
||||
}
|
||||
|
||||
// Check run strength
|
||||
const canRun = this.calculateRunStrength(deltaTime);
|
||||
this._actorMove!.speed = canRun ? -this._data.run_speed.z : -this._data.move_speed.z;
|
||||
const normalizeSpeed = Math.abs(this._actorMove!.velocity.length() / this._actorMove!.speed);
|
||||
this._actorEquipment?.updateAim(normalizeSpeed);
|
||||
this.recoverStrength();
|
||||
|
||||
// Update forward info.
|
||||
if (this._forwardNode) UtilVec3.copy(this.forward, this._forwardNode?.forward);
|
||||
}
|
||||
|
||||
onJump () {
|
||||
|
||||
if (this._actorSensorGround!._isGround === false) return;
|
||||
|
||||
if (this._data.strength >= this._data.cost_jump_strength) {
|
||||
this._data.strength -= this._data.cost_jump_strength;
|
||||
}
|
||||
|
||||
this.do('jump');
|
||||
}
|
||||
|
||||
onGround () {
|
||||
this.do('on_ground');
|
||||
}
|
||||
|
||||
offGround () {
|
||||
this.do('off_ground');
|
||||
}
|
||||
|
||||
onWin () { }
|
||||
|
||||
jump () {
|
||||
this._actorMove?.jump();
|
||||
}
|
||||
|
||||
onMove (move: Vec3) {
|
||||
this._actorMove?.moveDirection(move);
|
||||
}
|
||||
|
||||
onRotation (x: number, y: number) {
|
||||
|
||||
if (x > 90) x = 90;
|
||||
if (x < -90) x = -90;
|
||||
|
||||
this._actorMove?.onRotation(x, y);
|
||||
}
|
||||
|
||||
onDir (x: number, z: number) {
|
||||
this._dir.z = z;
|
||||
this._dir.x = x;
|
||||
}
|
||||
|
||||
onPause () { }
|
||||
|
||||
onRun (isRun: boolean) { this._data.is_run = isRun; }
|
||||
|
||||
onPick () {
|
||||
|
||||
var pickedNode = this.actorSensorDropItem?.getPicked();
|
||||
|
||||
if (pickedNode !== undefined) {
|
||||
|
||||
// Picked health.
|
||||
if (pickedNode.name == 'medkit') {
|
||||
const recoverHP = this._data.max_hp - this._data.hp;
|
||||
if (recoverHP <= 0) {
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('full_hp')}`
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this._data.hp = this._data.max_hp;
|
||||
Sound.on('sfx_recovery_hp')
|
||||
this.updateHP();
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('recovery_hp')} ${recoverHP}`
|
||||
);
|
||||
pickedNode.emit('picked');
|
||||
return;
|
||||
}
|
||||
|
||||
// Picked same weapon increase clip.
|
||||
if (this._actorBag?.pickedSameWeaponIncreaseClips(pickedNode.name)) {
|
||||
pickedNode.emit('picked');
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('picked_clip')} ${pickedNode.name} x 1`
|
||||
);
|
||||
Msg.emit('msg_update_bag');
|
||||
Msg.emit('msg_update_equip_info');
|
||||
return;
|
||||
}
|
||||
|
||||
// Picked bullet box.
|
||||
if (pickedNode.name === 'bullet_box') {
|
||||
this._actorBag?.pickedBulletBox()
|
||||
pickedNode.emit('picked');
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('picked_bullet_box')}`
|
||||
);
|
||||
Msg.emit('msg_update_equip_info');
|
||||
return;
|
||||
}
|
||||
|
||||
// Picked items
|
||||
if (this._actorBag?.pickedItem(pickedNode.name)) {
|
||||
pickedNode.emit('picked');
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('picked')} ${Local.Instance.get(pickedNode.name)} x 1`
|
||||
);
|
||||
this.bulletBox++;
|
||||
Msg.emit('msg_update_bag');
|
||||
Msg.emit('msg_update_equip_info');
|
||||
} else {
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('bag_is_full')}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
onDrop () {
|
||||
if (this._actorBag?.dropItem()) {
|
||||
Msg.emit('msg_update_bag');
|
||||
}
|
||||
}
|
||||
|
||||
onCrouch () {
|
||||
|
||||
this._data.is_crouch = this._data.is_crouch ? false : true;
|
||||
|
||||
// set view height.
|
||||
// set physic collider height.
|
||||
// set hit part height.
|
||||
this._animationGraph?.play('bool_crouch', this._data.is_crouch);
|
||||
Msg.emit('msg_change_tps_camera_height', this._data.is_crouch ? this._data.stand_camera_height : this._data.crouch_camera_height);
|
||||
|
||||
}
|
||||
|
||||
onAim (isAim: boolean | undefined) {
|
||||
if (isAim === undefined) {
|
||||
this._data.is_aim = this._data.is_aim ? false : true;
|
||||
} else {
|
||||
if (isAim == this._data.is_aim) return;
|
||||
this._data.is_aim = isAim;
|
||||
}
|
||||
|
||||
// Get aim state.
|
||||
this.do(this._data.is_aim ? 'on_aim' : 'off_aim');
|
||||
|
||||
if (this.isPlayer) Msg.emit('msg_change_tps_camera_target', this._data.is_aim ? 1 : 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Open fire interface.
|
||||
* @returns
|
||||
*/
|
||||
onFire () {
|
||||
|
||||
// Determines if the current equipment is ready to fire.
|
||||
// Includes the number of rounds in the magazine and the firing cooldown.
|
||||
if (this._actorEquipment!.currentEquip?.checkUse() == false) return;
|
||||
|
||||
// Execute the fir action.
|
||||
this._actorEquipment?.do('fire');
|
||||
|
||||
// Sets the aim stable value to the maximum.
|
||||
this._actorEquipment?.updateAim(1, true);
|
||||
|
||||
/*
|
||||
const canUseEquip = this.calculateStrengthUseEquip();
|
||||
if (canUseEquip) {}
|
||||
*/
|
||||
}
|
||||
|
||||
/**
|
||||
* Set Auto fire state.
|
||||
* @param isAutoFire Auto fire state.
|
||||
*/
|
||||
onAutoFire (isAutoFire: boolean) { this._data.is_auto_fire = isAutoFire; }
|
||||
|
||||
onReload () {
|
||||
|
||||
if (this._actorEquipment?.currentEquip?.checkFullBullet()) return;
|
||||
|
||||
this._actorEquipment?.do('reload');
|
||||
}
|
||||
|
||||
onEquip (index: number) {
|
||||
|
||||
if (this._actorEquipment?.equip(index)) {
|
||||
if (this._data.has_multi_res) this._viewNoWeapon.active = false;
|
||||
} else {
|
||||
if (this._data.has_multi_res) this._viewNoWeapon.active = true;
|
||||
}
|
||||
}
|
||||
|
||||
onChangeEquips (): boolean { return false; }
|
||||
|
||||
calculateStrengthUseEquip (): boolean {
|
||||
|
||||
const canUseEquip = this._data.strength >= this._data.cost_use_equip_strength;
|
||||
if (canUseEquip) {
|
||||
this._data.strength -= this._data.cost_use_equip_strength;
|
||||
this._data.strength = Math.max(this._data.strength, 0);
|
||||
}
|
||||
|
||||
return canUseEquip;
|
||||
}
|
||||
|
||||
calculateRunStrength (deltaTime: number): boolean {
|
||||
const canRun = this._data.is_run && this._data.strength >= this._data.cost_run_strength;
|
||||
if (canRun) {
|
||||
this._data.strength -= this._data.cost_run_strength * deltaTime;
|
||||
this._data.strength = Math.max(this._data.strength, 0);
|
||||
}
|
||||
return canRun;
|
||||
}
|
||||
|
||||
recoverStrength () {
|
||||
if (this._data.is_ground === false) return;
|
||||
if (this._data.is_run) return;
|
||||
|
||||
this._data.strength += this._data.recover_ground_strength * game.deltaTime;
|
||||
if (this._data.strength > this._data.max_strength) this._data.strength = this._data.max_strength;
|
||||
const percent_value = this._data.strength / this._data.max_strength;
|
||||
|
||||
if (this.isPlayer) {
|
||||
Msg.emit('fil_strength', percent_value);
|
||||
}
|
||||
}
|
||||
|
||||
lateUpdate (deltaTime: number) {
|
||||
|
||||
if (this._actorMove == undefined) return;
|
||||
|
||||
// Synchronize animation setup data.
|
||||
const rigidBody = this._actorMove?.rigid;
|
||||
rigidBody!.getLinearVelocity(tempLinearVelocity);
|
||||
|
||||
tempLinearVelocity.y = 0;
|
||||
const linearVelocityLength = tempLinearVelocity.length();
|
||||
const eulerAnglesY = this.node.eulerAngles.y;
|
||||
|
||||
//rotate y.
|
||||
Vec3.rotateY(tempLinearVelocity, tempLinearVelocity, Vec3.ZERO, math.toRadian(-eulerAnglesY));
|
||||
|
||||
let num_velocity_x = tempLinearVelocity.x;
|
||||
let num_velocity_y = tempLinearVelocity.z;
|
||||
|
||||
let moveSpeed = linearVelocityLength * this._data.linear_velocity_animation_rate;
|
||||
|
||||
// Check rotation.
|
||||
const angleSpeed = this._actorMove!.angle;
|
||||
if (linearVelocityLength < 0.01 && angleSpeed > 2) {
|
||||
moveSpeed = angleSpeed * this._data.angle_velocity_animation_rate;
|
||||
num_velocity_x = angleSpeed / this._data.angle_velocity_animation_scale;
|
||||
}
|
||||
|
||||
this._animationGraph?.setValue('num_velocity_x', num_velocity_x);
|
||||
this._animationGraph?.setValue('num_velocity_y', -num_velocity_y);
|
||||
this._animationGraph?.setValue('num_move_speed', moveSpeed);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "93422fc8-69fa-4ad1-9748-d74807c38cab",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
import { _decorator, Component, Node, animation, CCFloat, CCString } from "cc";
|
||||
import { Msg } from "../../core/msg/msg";
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass("AnimationGraphMove")
|
||||
export class AnimationGraphMove extends animation.StateMachineComponent {
|
||||
|
||||
|
||||
@property
|
||||
time = 0.5;
|
||||
|
||||
@property
|
||||
msg = 'msg_walk_sfx';
|
||||
|
||||
_triggered: boolean = false;
|
||||
|
||||
/**
|
||||
* Called right after a motion state is entered.
|
||||
* @param controller The animation controller it within.
|
||||
* @param motionStateStatus The status of the motion.
|
||||
*/
|
||||
public onMotionStateEnter (controller: animation.AnimationController, motionStateStatus: Readonly<animation.MotionStateStatus>): void {
|
||||
// Can be overrode
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a motion state is about to exit.
|
||||
* @param controller The animation controller it within.
|
||||
* @param motionStateStatus The status of the motion.
|
||||
*/
|
||||
public onMotionStateExit (controller: animation.AnimationController, motionStateStatus: Readonly<animation.MotionStateStatus>): void {
|
||||
// Can be overrode
|
||||
this._triggered = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a motion state updated except for the first and last frame.
|
||||
* @param controller The animation controller it within.
|
||||
* @param motionStateStatus The status of the motion.
|
||||
*/
|
||||
public onMotionStateUpdate (controller: animation.AnimationController, motionStateStatus: Readonly<animation.MotionStateStatus>): void {
|
||||
// Can be overrode
|
||||
if (motionStateStatus.progress > this.time && !this._triggered) {
|
||||
// 触发事件
|
||||
this._triggered = true;
|
||||
Msg.emit(this.msg);
|
||||
} else if (motionStateStatus.progress < this.time && this._triggered) {
|
||||
this._triggered = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Called right after a state machine is entered.
|
||||
* @param controller The animation controller it within.
|
||||
*/
|
||||
public onStateMachineEnter (controller: animation.AnimationController) {
|
||||
// Can be overrode
|
||||
}
|
||||
|
||||
/**
|
||||
* Called right after a state machine is entered.
|
||||
* @param controller The animation controller it within.
|
||||
*/
|
||||
public onStateMachineExit (controller: animation.AnimationController) {
|
||||
// Can be overrode
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "4cc78b6c-8111-4ea7-acfd-b19d390703f8",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
import { Node, PhysicsRayResult, Vec3 } from "cc";
|
||||
import { Sound } from "../../core/audio/sound";
|
||||
import { fx } from "../../core/effect/fx";
|
||||
import { Local } from "../../core/localization/local";
|
||||
import { Msg } from "../../core/msg/msg";
|
||||
import { ActorPart } from "./actor-part";
|
||||
import { Actor } from "./actor";
|
||||
import { DataLevelInst } from "../data/data-core";
|
||||
|
||||
export function calculateDamageNode (data: any, node: Node, hitPoint: Vec3, shootActor: Actor | undefined) {
|
||||
const hitName = node.name.split('_')[0];
|
||||
let hitTag = `hit_${hitName}`;
|
||||
|
||||
const damage = data.damage;
|
||||
const actorPart = node.getComponent(ActorPart);
|
||||
|
||||
if (shootActor?.isPlayer) Msg.emit('msg_stat_times', `enemy_fire`);
|
||||
|
||||
if (actorPart) {
|
||||
const actorBodyName = actorPart.part;
|
||||
const part_damage = damage[actorBodyName];
|
||||
if (part_damage === undefined) throw new Error(`${node.name} node hit part undefine '${actorBodyName}'`);
|
||||
|
||||
const actor = actorPart.actor;
|
||||
if (actor === undefined) throw new Error(`${node.name} node hit part '${actorBodyName}' undefine actor`);
|
||||
|
||||
if (shootActor?.isPlayer) {
|
||||
Msg.emit('msg_stat_times', `hit_${actorBodyName}`);
|
||||
}
|
||||
|
||||
if (actor.isPlayer) {
|
||||
Msg.emit('msg_stat_times', `be_hit_${actorBodyName}`);
|
||||
}
|
||||
|
||||
actor._data.hp -= part_damage;
|
||||
if (actor._data.hp <= 0) {
|
||||
actor._data.hp = 0;
|
||||
fx.on(DataLevelInst._data.fx_dead, actor.node.worldPosition);
|
||||
if (actor.isPlayer) Msg.emit('msg_stat_times', 'killed');
|
||||
actor.do('dead');
|
||||
} else {
|
||||
actor.do('hit_gun');
|
||||
}
|
||||
|
||||
if (actor.isPlayer) actor.updateHP();
|
||||
|
||||
hitTag = 'hit_body';
|
||||
}
|
||||
calculateDamageView(damage[hitTag], hitPoint);
|
||||
}
|
||||
|
||||
export function calculateDamage (data: any, hit: PhysicsRayResult | undefined, shootActor: Actor | undefined) {
|
||||
|
||||
if (shootActor?.isPlayer) Msg.emit('msg_stat_times', `enemy_fire`);
|
||||
|
||||
if (hit === undefined) {
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get('hit_nothing')}`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
const node: Node = hit.collider.node;
|
||||
const hitName = node.name.split('_')[0];
|
||||
let hitTag = `hit_${hitName}`;
|
||||
|
||||
const damage = data.damage;
|
||||
const actorPart = node.getComponent(ActorPart);
|
||||
|
||||
if (actorPart) {
|
||||
const actorBodyName = actorPart.part;
|
||||
const part_damage = damage[actorBodyName];
|
||||
if (part_damage === undefined) throw new Error(`${node.name} node hit part undefine '${actorBodyName}'`);
|
||||
|
||||
const actor = actorPart.actor;
|
||||
if (actor === undefined) throw new Error(`${node.name} node hit part '${actorBodyName}' undefine actor`);
|
||||
|
||||
if (shootActor?.isPlayer) {
|
||||
Msg.emit('msg_stat_times', `hit_${actorBodyName}`);
|
||||
}
|
||||
|
||||
if (actor.isPlayer) {
|
||||
Msg.emit('msg_stat_times', `be_hit_${actorBodyName}`);
|
||||
}
|
||||
|
||||
actor._data.hp -= part_damage;
|
||||
if (actor._data.hp <= 0) {
|
||||
actor._data.hp = 1;
|
||||
fx.on(DataLevelInst._data.fx_dead, actor.node.worldPosition);
|
||||
if (shootActor?.isPlayer) Msg.emit('msg_stat_times', 'killed');
|
||||
actor.do('dead');
|
||||
|
||||
} else {
|
||||
actor.do('hit_gun')
|
||||
}
|
||||
hitTag = 'hit_body';
|
||||
|
||||
if (actor.isPlayer) actor.updateHP();
|
||||
}
|
||||
calculateDamageView(damage[hitTag], hit.hitPoint);
|
||||
}
|
||||
|
||||
function calculateDamageView (damage: Record<string, any> | undefined, hitPoint: Vec3) {
|
||||
if (damage === undefined) return;
|
||||
if (damage.fx) fx.on(damage.fx, hitPoint);
|
||||
if (damage.sfx) Sound.on(damage.sfx);
|
||||
if (damage.notify === undefined) {
|
||||
const showMsg = damage['notify'];
|
||||
if (showMsg == undefined) return;
|
||||
Msg.emit(
|
||||
'msg_tips',
|
||||
`${Local.Instance.get(damage['notify'])}`
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "d8d6abd9-1319-4ee5-88f5-8656e35e0e4b",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
import { _decorator, Color, Component, geometry, Node, PhysicsRayResult, PhysicsSystem, v3, Vec2, Vec3, CCFloat } from 'cc';
|
||||
import { Gizmo, Util, UtilVec3 } from '../../core/util/util';
|
||||
import { FxRayLine } from '../effect/fx-ray-line';
|
||||
import { EDITOR } from 'cc/env';
|
||||
const { ccclass, property, executeInEditMode } = _decorator;
|
||||
|
||||
@ccclass('InfraredTracker')
|
||||
@executeInEditMode
|
||||
export class InfraredTracker extends Component {
|
||||
|
||||
@property([CCFloat])
|
||||
masks: number[] = [];
|
||||
|
||||
@property
|
||||
distance = 300;
|
||||
|
||||
@property(Node)
|
||||
forwardNode: Node | undefined;
|
||||
|
||||
ray: geometry.Ray | undefined;
|
||||
|
||||
target: Node | undefined;
|
||||
|
||||
mask: number = 0;
|
||||
|
||||
hit: PhysicsRayResult | undefined;
|
||||
|
||||
endPosition = v3(0, 0, 0);
|
||||
|
||||
rayLine: FxRayLine | undefined;
|
||||
|
||||
forward = v3(0, 0, 0);
|
||||
|
||||
direction = v3(0, 0, 0);
|
||||
|
||||
onEnable () {
|
||||
this.ray = new geometry.Ray();
|
||||
this.mask = Util.calculateMask(this.masks);
|
||||
this.rayLine = this.node.children[0].getComponent(FxRayLine)!;
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
UtilVec3.copy(this.ray!.o, this.node.worldPosition);
|
||||
UtilVec3.copy(this.direction, this.forwardNode!.worldPosition);
|
||||
this.direction.subtract(this.node.worldPosition).normalize();
|
||||
UtilVec3.copy(this.ray!.d, this.direction);
|
||||
this.hit = undefined;
|
||||
this.target = undefined;
|
||||
if (PhysicsSystem.instance.raycastClosest(this.ray!, this.mask, this.distance)) {
|
||||
this.hit = PhysicsSystem.instance.raycastClosestResult;
|
||||
}
|
||||
|
||||
if (this.hit !== undefined) {
|
||||
this.target = this.hit.collider.node;
|
||||
UtilVec3.copy(this.endPosition, this.hit.hitPoint);
|
||||
} else {
|
||||
UtilVec3.copy(this.endPosition, this.node.worldPosition);
|
||||
//console.log('InfraredTracker forward:', this.direction);
|
||||
UtilVec3.scaleDirection(this.endPosition, this.direction, this.distance);
|
||||
}
|
||||
|
||||
// Update ray line.
|
||||
this.rayLine?.setRayLine(this.node.worldPosition, this.endPosition);
|
||||
|
||||
if (EDITOR) Gizmo.drawLine(this.ray!.o, this.endPosition, Color.BLUE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "e6e9cce9-5439-4aa0-a61f-9e3efb73a9f0",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import { _decorator, Component, animation } from 'cc';
|
||||
import { Level } from '../level/level';
|
||||
import { Game } from '../data/game';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('LevelEnablePlay')
|
||||
export class LevelEnablePlay extends Component {
|
||||
|
||||
onEnable() {
|
||||
if (Game.Instance._currentGameNodeName !== 'level') {
|
||||
this.node.getComponent(animation.AnimationController)!.enabled = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "35054940-62df-45b3-97fc-33648e5b206f",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
import { _decorator, Collider, Component, ITriggerEvent, Node, PhysicsSystem, RigidBody, v3, Vec3, CCFloat } from 'cc';
|
||||
import { calculateDamageNode } from './damage-core';
|
||||
import { Sound } from '../../core/audio/sound';
|
||||
import { Actor } from './actor';
|
||||
const { ccclass, property } = _decorator;
|
||||
|
||||
@ccclass('ProjectileGrenade')
|
||||
export class ProjectileGrenade extends Component {
|
||||
|
||||
_data: any;
|
||||
|
||||
_size = v3(1, 1, 1);
|
||||
|
||||
@property(CCFloat)
|
||||
explodeTime = 3;
|
||||
|
||||
@property(Vec3)
|
||||
endSize = v3(6, 6, 6);
|
||||
|
||||
@property(Collider)
|
||||
collider: Collider | undefined;
|
||||
|
||||
@property(RigidBody)
|
||||
rigidbody: RigidBody | undefined;
|
||||
|
||||
updateFunction: Function | undefined;
|
||||
|
||||
actor: Actor | undefined;
|
||||
|
||||
onThrow (weaponData: any, force: Vec3, shootActor: Actor | undefined) {
|
||||
this._data = weaponData;
|
||||
this.actor = shootActor;
|
||||
this.rigidbody?.applyImpulse(force);
|
||||
this.updateFunction = this.waitExplode;
|
||||
}
|
||||
|
||||
onExplode () {
|
||||
this.collider!.isTrigger = true;
|
||||
this.rigidbody!.useGravity = false;
|
||||
this.updateFunction = this.exploding;
|
||||
this.collider!.on('onTriggerEnter', this.onTriggerEnter, this);
|
||||
Sound.on(this._data.sound_explode);
|
||||
}
|
||||
|
||||
onExplodeEnd () {
|
||||
this.updateFunction = undefined;
|
||||
this.collider!.off('onTriggerEnter', this.onTriggerEnter, this);
|
||||
this.node.active = false;
|
||||
}
|
||||
|
||||
onTriggerEnter (event: ITriggerEvent) {
|
||||
const hitPoint = event.otherCollider.node.getWorldPosition();
|
||||
calculateDamageNode(this._data, event.otherCollider.node, hitPoint, this.actor);
|
||||
}
|
||||
|
||||
waitExplode (deltaTime: number) {
|
||||
this.explodeTime -= deltaTime;
|
||||
if (this.explodeTime <= 0) {
|
||||
this.onExplode();
|
||||
}
|
||||
}
|
||||
|
||||
exploding (deltaTime: number) {
|
||||
Vec3.lerp(this._size, this._size, this.endSize, deltaTime * 5);
|
||||
this.node.setWorldScale(this._size);
|
||||
if (Math.abs(this._size.x - this.endSize.x) < 0.1) {
|
||||
this.onExplodeEnd();
|
||||
}
|
||||
}
|
||||
|
||||
update (deltaTime: number) {
|
||||
if (this.updateFunction !== undefined)
|
||||
this.updateFunction(deltaTime);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"ver": "4.0.23",
|
||||
"importer": "typescript",
|
||||
"imported": true,
|
||||
"uuid": "b56c0328-39d9-47c8-aaeb-b344a12fe602",
|
||||
"files": [],
|
||||
"subMetas": {},
|
||||
"userData": {}
|
||||
}
|
||||
Reference in New Issue
Block a user