mirror of
https://github.com/MonsterDruide1/OdysseyDecomp
synced 2026-04-23 09:04:21 +00:00
550 lines
17 KiB
C++
550 lines
17 KiB
C++
#include "Enemy/Gamane.h"
|
|
|
|
#include <math/seadVector.h>
|
|
|
|
#include "Library/Collision/KCollisionServer.h"
|
|
#include "Library/Effect/EffectSystemInfo.h"
|
|
#include "Library/Item/ItemUtil.h"
|
|
#include "Library/LiveActor/ActorActionFunction.h"
|
|
#include "Library/LiveActor/ActorAnimFunction.h"
|
|
#include "Library/LiveActor/ActorAreaFunction.h"
|
|
#include "Library/LiveActor/ActorClippingFunction.h"
|
|
#include "Library/LiveActor/ActorCollisionFunction.h"
|
|
#include "Library/LiveActor/ActorInitUtil.h"
|
|
#include "Library/LiveActor/ActorModelFunction.h"
|
|
#include "Library/LiveActor/ActorMovementFunction.h"
|
|
#include "Library/LiveActor/ActorPoseUtil.h"
|
|
#include "Library/LiveActor/ActorSensorUtil.h"
|
|
#include "Library/Math/MathUtil.h"
|
|
#include "Library/Movement/EnemyStateBlowDown.h"
|
|
#include "Library/Nerve/NerveSetupUtil.h"
|
|
#include "Library/Nerve/NerveUtil.h"
|
|
#include "Library/Player/PlayerUtil.h"
|
|
#include "Library/Shadow/ActorShadowUtil.h"
|
|
|
|
#include "Enemy/EnemyStateHackStart.h"
|
|
#include "Enemy/EnemyStateSwoon.h"
|
|
#include "Enemy/GamaneBullet.h"
|
|
#include "Enemy/GamaneHackState.h"
|
|
#include "Enemy/HackerDepthShadowMapCtrl.h"
|
|
#include "Player/HackerJudgeNormalFall.h"
|
|
#include "Player/PlayerHackStartShaderCtrl.h"
|
|
#include "Util/Hack.h"
|
|
#include "Util/ItemUtil.h"
|
|
#include "Util/JudgeUtil.h"
|
|
#include "Util/SensorMsgFunction.h"
|
|
#include "Util/ShadowUtil.h"
|
|
|
|
namespace {
|
|
NERVE_IMPL(Gamane, Wait)
|
|
NERVE_IMPL(Gamane, Find)
|
|
NERVE_IMPL(Gamane, Runaway)
|
|
NERVE_IMPL(Gamane, Fall)
|
|
NERVE_IMPL(Gamane, Land)
|
|
NERVE_IMPL(Gamane, Swoon)
|
|
NERVE_IMPL(Gamane, HackStart)
|
|
NERVE_IMPL(Gamane, Hack)
|
|
NERVE_IMPL(Gamane, Trampled)
|
|
NERVE_IMPL(Gamane, PressDown)
|
|
NERVE_IMPL(Gamane, BlowDown)
|
|
|
|
NERVES_MAKE_NOSTRUCT(Gamane, Land)
|
|
NERVES_MAKE_STRUCT(Gamane, Wait, Swoon, Hack, BlowDown, HackStart, Trampled, PressDown, Fall, Find,
|
|
Runaway)
|
|
} // namespace
|
|
|
|
static al::EnemyStateBlowDownParam gEnemyStateBlowDownParam =
|
|
al::EnemyStateBlowDownParam("BlowDown", 18.0f, 35.0f, 1.0f, 0.98f, 120, true);
|
|
|
|
static PlayerHackStartShaderParam gPlayerHackStartShaderParam =
|
|
PlayerHackStartShaderParam(false, 300.0f, 10, 20);
|
|
|
|
static EnemyStateSwoonInitParam gEnemyStateSwoonInitParam = EnemyStateSwoonInitParam(
|
|
"SwoonStart", "Swoon", "SwoonEnd", nullptr, "SwoonStartFall", "SwoonStartLand");
|
|
|
|
Gamane::Gamane(const char* name) : al::LiveActor(name) {}
|
|
|
|
void Gamane::init(const al::ActorInitInfo& info) {
|
|
al::initActorWithArchiveName(this, info, "Gamane", nullptr);
|
|
al::initNerve(this, &NrvGamane.Wait, 5);
|
|
mCapTargetInfo = rs::createCapTargetInfo(this, nullptr);
|
|
|
|
mStateSwoon = new EnemyStateSwoon(this, "SwoonStart", "Swoon", "SwoonEnd", false, true);
|
|
|
|
gEnemyStateSwoonInitParam.swoonDuration = 180;
|
|
gEnemyStateSwoonInitParam.hitReactionAnimName = "着地";
|
|
mStateSwoon->initParams(gEnemyStateSwoonInitParam);
|
|
mStateSwoon->enableLockOnDelay(true);
|
|
|
|
al::initNerveState(this, mStateSwoon, &NrvGamane.Swoon, "気絶");
|
|
|
|
mHackState = new GamaneHackState(this);
|
|
al::initNerveState(this, mHackState, &NrvGamane.Hack, "憑依");
|
|
mHackState->initialize(info);
|
|
|
|
mStateBlowDown = new al::EnemyStateBlowDown(this, &gEnemyStateBlowDownParam, "吹き飛び状態");
|
|
al::initNerveState(this, mStateBlowDown, &NrvGamane.BlowDown, "吹き飛び");
|
|
|
|
mJudgeNormalFall = new HackerJudgeNormalFall(this, 5);
|
|
|
|
mCollisionPartsFilter = new al::CollisionPartsFilterSpecialPurpose("MoveLimit");
|
|
al::setColliderFilterCollisionParts(this, mCollisionPartsFilter);
|
|
|
|
mStateHackStart = new EnemyStateHackStart(this, nullptr, &gPlayerHackStartShaderParam);
|
|
al::initNerveState(this, mStateHackStart, &NrvGamane.HackStart, "ひょうい開始");
|
|
|
|
al::setMaterialProgrammable(this);
|
|
mMaterialIndex = al::getMaterialIndex(this, "TransMT");
|
|
mShadowMaskIntensity = al::getShadowMaskIntensity(this, "シャドウマスク");
|
|
al::hideShadowMask(this);
|
|
|
|
al::startVisAnim(this, "Off");
|
|
al::startVisAnim(this, "CapOff");
|
|
al::startMtpAnim(this, "CapOff");
|
|
al::setModelMaterialParameterF32(this, mMaterialIndex, "const_single0", 1.0f);
|
|
makeActorAlive();
|
|
|
|
rs::initHackShadow(this);
|
|
mDepthShadowMapCtrl = new HackerDepthShadowMapCtrl(this, "Ground", 50.0f, 0.3f, 0.5f);
|
|
}
|
|
|
|
void Gamane::attackSensor(al::HitSensor* self, al::HitSensor* other) {
|
|
if (al::isNerve(this, &NrvGamane.PressDown) || al::isNerve(this, &NrvGamane.BlowDown))
|
|
return;
|
|
|
|
if (mPlayerHack) {
|
|
mHackState->attackSensor(self, other);
|
|
return;
|
|
}
|
|
|
|
if (al::isSensorEnemyBody(self) && al::isSensorEnemyBody(other))
|
|
al::sendMsgPushAndKillVelocityToTarget(this, self, other);
|
|
|
|
if (al::isSensorEnemyBody(self)) {
|
|
al::sendMsgPush(other, self);
|
|
rs::sendMsgPushToPlayer(other, self);
|
|
}
|
|
}
|
|
|
|
bool Gamane::receiveMsg(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
|
|
if ((!al::isSensorEnemyBody(self) || al::isNerve(this, &NrvGamane.PressDown) ||
|
|
al::isNerve(this, &NrvGamane.BlowDown)) &&
|
|
(al::isMsgPlayerDisregard(message) || rs::isMsgPlayerDisregardHomingAttack(message) ||
|
|
rs::isMsgPlayerDisregardTargetMarker(message)))
|
|
return true;
|
|
|
|
if (al::isNerve(this, &NrvGamane.PressDown))
|
|
return false;
|
|
|
|
if (al::isNerve(this, &NrvGamane.BlowDown))
|
|
return false;
|
|
|
|
if (rs::tryReceiveMsgInitCapTargetAndSetCapTargetInfo(message, mCapTargetInfo))
|
|
return true;
|
|
|
|
if (!al::isNerve(this, &NrvGamane.Hack) && !al::isNerve(this, &NrvGamane.HackStart) &&
|
|
!al::isNerve(this, &NrvGamane.Swoon)) {
|
|
if (rs::isMsgCapEnableLockOn(message) || rs::isMsgCapCancelLockOn(message) ||
|
|
mStateSwoon->tryReceiveMsgStartLockOn(message))
|
|
return true;
|
|
if (mStateSwoon->tryReceiveMsgStartHack(message)) {
|
|
startHack(message, other, self);
|
|
al::setNerve(this, &NrvGamane.HackStart);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (al::isNerve(this, &NrvGamane.Swoon)) {
|
|
if (mStateSwoon->tryReceiveMsgEnableLockOn(message))
|
|
return true;
|
|
if (mStateSwoon->tryReceiveMsgStartHack(message)) {
|
|
startHack(message, other, self);
|
|
al::setNerve(this, &NrvGamane.HackStart);
|
|
return true;
|
|
}
|
|
if (mStateSwoon->tryReceiveMsgEndSwoon(message))
|
|
return true;
|
|
}
|
|
|
|
if (mPlayerHack) {
|
|
if (rs::isMsgHackMarioCheckpointFlagWarp(message)) {
|
|
rs::endHack(&mPlayerHack);
|
|
rs::endHackShadow(this);
|
|
al::startVisAnim(this, "CapOff");
|
|
al::startMtpAnim(this, "CapOff");
|
|
al::setColliderFilterCollisionParts(this, mCollisionPartsFilter);
|
|
return true;
|
|
}
|
|
|
|
bool result = mHackState->receiveMsg(message, other, self);
|
|
if (mHackState->isHackEnd()) {
|
|
mPlayerHack = nullptr;
|
|
rs::endHackShadow(this);
|
|
al::startVisAnim(this, "CapOff");
|
|
al::startMtpAnim(this, "CapOff");
|
|
al::setColliderFilterCollisionParts(this, mCollisionPartsFilter);
|
|
al::setNerve(this, &NrvGamane.Swoon);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
if ((al::isSensorEnemyBody(other) || al::isSensorMapObj(other)) &&
|
|
al::tryReceiveMsgPushAndAddVelocity(this, message, other, self, 3.0f))
|
|
return true;
|
|
|
|
if (al::isMsgPlayerTrampleReflect(message) || rs::isMsgSenobiTrample(message)) {
|
|
rs::requestHitReactionToAttacker(message, self, other);
|
|
if (mCoinsLeft > 0) {
|
|
rs::tryAppearMultiCoinFromObj(this, al::getHitSensor(this, "Body"), 0, 150.0f);
|
|
mCoinsLeft--;
|
|
}
|
|
endRefract(25);
|
|
if (al::isNerve(this, &NrvGamane.Swoon)) {
|
|
mStateSwoon->requestTrampled();
|
|
return true;
|
|
}
|
|
al::setNerve(this, &NrvGamane.Trampled);
|
|
return true;
|
|
}
|
|
|
|
if (rs::isMsgPressDown(message) && !al::isMsgPlayerTrample(message)) {
|
|
rs::requestHitReactionToAttacker(message, self, other);
|
|
al::setNerve(this, &NrvGamane.PressDown);
|
|
return true;
|
|
}
|
|
|
|
if (rs::isMsgBlowDown(message)) {
|
|
if (rs::isMsgGamaneBullet(message)) {
|
|
GamaneBullet* bullet = (GamaneBullet*)al::getSensorHost(other);
|
|
if (bullet && bullet->getParent() == this)
|
|
return false;
|
|
}
|
|
|
|
rs::requestHitReactionToAttacker(message, self, other);
|
|
mStateBlowDown->start(other);
|
|
al::setNerve(this, &NrvGamane.BlowDown);
|
|
return true;
|
|
}
|
|
|
|
if (rs::isMsgKillByShineGet(message) || rs::isMsgKillByHomeDemo(message)) {
|
|
al::tryKillEmitterAndParticleAll(this);
|
|
makeActorDead();
|
|
return true;
|
|
}
|
|
|
|
if (rs::tryReceiveMsgNpcScareByEnemyIgnoreTargetHack(message, mCapTargetInfo))
|
|
return true;
|
|
|
|
if (al::isMsgEnemyAttackFire(message)) {
|
|
rs::requestHitReactionToAttacker(message, self, other);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Gamane::control() {
|
|
updateRefract();
|
|
if (al::isCollidedGround(this)) {
|
|
al::setMaterialCode(this, al::getCollidedFloorMaterialCodeName(this));
|
|
al::updateMaterialCodePuddle(this);
|
|
}
|
|
|
|
if (al::isNerve(this, &NrvGamane.Hack) && mPlayerHack)
|
|
mDepthShadowMapCtrl->update(nullptr);
|
|
|
|
if (!al::isNerve(this, &NrvGamane.Hack) || al::isHideModel(this))
|
|
al::hideSilhouetteModelIfShow(this);
|
|
else
|
|
al::showSilhouetteModelIfHide(this);
|
|
|
|
if (al::isInDeathArea(this) || al::isInWaterArea(this) ||
|
|
al::isCollidedFloorCode(this, "DamageFire") || al::isCollidedFloorCode(this, "Poison")) {
|
|
if (!al::isInDeathArea(this) && mPlayerHack)
|
|
rs::endHack(&mPlayerHack);
|
|
|
|
al::startHitReaction(this, "消滅");
|
|
kill();
|
|
}
|
|
}
|
|
|
|
void Gamane::endClipped() {
|
|
al::LiveActor::endClipped();
|
|
if (mIsStartRefract)
|
|
al::hideShadowMask(this);
|
|
}
|
|
|
|
void Gamane::updateCollider() {
|
|
if (al::isNerve(this, &NrvGamane.HackStart))
|
|
return;
|
|
|
|
if (al::isNerve(this, &NrvGamane.Hack)) {
|
|
sead::Vector3f hackVelocity = mHackState->getVelocity();
|
|
sead::Vector3f velocity = al::getVelocity(this);
|
|
velocity += hackVelocity;
|
|
al::setVelocity(this, velocity);
|
|
mHackState->setVelocity(sead::Vector3f(0.0f, 0.0f, 0.0f));
|
|
}
|
|
|
|
al::LiveActor::updateCollider();
|
|
}
|
|
|
|
void Gamane::startHack(const al::SensorMsg* message, al::HitSensor* other, al::HitSensor* self) {
|
|
al::invalidateClipping(this);
|
|
mPlayerHack = mStateHackStart->tryStart(message, other, self);
|
|
mHackState->setPlayerHackAction(mPlayerHack);
|
|
rs::hideShadowHackCap(mPlayerHack);
|
|
rs::setupHackShadow(this);
|
|
al::setColliderFilterCollisionParts(this, nullptr);
|
|
endRefract(50);
|
|
}
|
|
|
|
void Gamane::updateRefract() {
|
|
if (mRefractTransitionTime == 0) {
|
|
al::setShadowMaskIntensity(this, "シャドウマスク", mShadowMaskIntensity);
|
|
return;
|
|
}
|
|
|
|
f32 refractPercentage = mRefractTransitionTime / 50.0f;
|
|
if (mIsStartRefract != false)
|
|
refractPercentage = 1.0f - refractPercentage;
|
|
al::setModelMaterialParameterF32(this, mMaterialIndex, "const_single0", refractPercentage);
|
|
al::setModelMaterialParameterF32(this, mMaterialIndex, "const_single2",
|
|
refractPercentage * 0.5);
|
|
mRefractTransitionTime--;
|
|
|
|
if (mPlayerHack)
|
|
return;
|
|
|
|
f32 intensity = al::lerpValue(mShadowMaskIntensity, 1.0, refractPercentage);
|
|
al::setShadowMaskIntensity(this, "シャドウマスク", intensity);
|
|
if (mRefractTransitionTime == 0) {
|
|
if (mIsStartRefract) {
|
|
al::hideShadowMask(this);
|
|
al::validateClipping(this);
|
|
} else {
|
|
al::startVisAnim(this, "On");
|
|
al::invalidateClipping(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void Gamane::updateMovement() {
|
|
sead::Vector3f gravityDir = sead::Vector3f(0.0f, 0.0f, 0.0f);
|
|
al::calcGravityDir(&gravityDir, this);
|
|
|
|
if (al::isOnGround(this, 0))
|
|
al::scaleVelocityDirection(this, gravityDir, 0.0f);
|
|
|
|
al::scaleVelocityParallelVertical(this, gravityDir, 0.99f, 0.1f);
|
|
al::addVelocityToGravityNaturalOrFittedGround(this, 1.5f);
|
|
}
|
|
|
|
void Gamane::startRefract(s32 transitionTime) {
|
|
if (mRefractTransitionTime < 1 && !mIsStartRefract) {
|
|
mRefractTransitionTime = transitionTime;
|
|
mIsStartRefract = true;
|
|
al::startVisAnim(this, "Off");
|
|
}
|
|
}
|
|
|
|
void Gamane::endRefract(s32 transitionTime) {
|
|
if (mRefractTransitionTime < 1 && mIsStartRefract) {
|
|
mRefractTransitionTime = transitionTime;
|
|
mIsStartRefract = false;
|
|
al::showShadowMask(this);
|
|
}
|
|
}
|
|
|
|
void Gamane::exeWait() {
|
|
if (al::isFirstStep(this)) {
|
|
al::setVelocityZero(this);
|
|
al::startAction(this, "Wait");
|
|
rs::resetJudge(mJudgeNormalFall);
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (rs::updateJudgeAndResult(mJudgeNormalFall)) {
|
|
al::setNerve(this, &NrvGamane.Fall);
|
|
return;
|
|
}
|
|
|
|
if (al::isStep(this, 180))
|
|
startRefract(50);
|
|
|
|
if (al::calcDistanceH(this, al::getPlayerActor(this, 0)) < 1000.0f)
|
|
al::setNerve(this, &NrvGamane.Find);
|
|
}
|
|
|
|
void Gamane::exeFind() {
|
|
if (al::isFirstStep(this)) {
|
|
al::startAction(this, "Find");
|
|
al::setVelocityZero(this);
|
|
al::faceToTarget(this, al::getPlayerActor(this, 0));
|
|
endRefract(50);
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (al::isActionEnd(this)) {
|
|
al::startVisAnim(this, "On");
|
|
al::setNerve(this, &NrvGamane.Runaway);
|
|
}
|
|
}
|
|
|
|
void Gamane::exeRunaway() {
|
|
if (al::isFirstStep(this)) {
|
|
al::startAction(this, "Run");
|
|
rs::resetJudge(mJudgeNormalFall);
|
|
mRunAwayRefractDelay = 0;
|
|
}
|
|
|
|
sead::Vector3f dirToActor;
|
|
al::calcDirToActorH(&dirToActor, this, al::getPlayerActor(this, 0));
|
|
dirToActor.negate();
|
|
al::turnToDirection(this, dirToActor, 4.0f);
|
|
|
|
sead::Vector3f quatFront;
|
|
al::calcQuatFront(&quatFront, this);
|
|
al::addVelocityToDirection(this, quatFront, 0.4f);
|
|
|
|
sead::Vector3f gravityDir = sead::Vector3f(0.0f, 0.0f, 0.0f);
|
|
al::calcGravityDir(&gravityDir, this);
|
|
|
|
if (al::isOnGround(this, 0))
|
|
al::scaleVelocityDirection(this, gravityDir, 0.0f);
|
|
|
|
al::scaleVelocityParallelVertical(this, gravityDir, 0.99f, 0.95f);
|
|
al::addVelocityToGravityNaturalOrFittedGround(this, 1.8f);
|
|
|
|
if (!mIsStartRefract)
|
|
mRunAwayRefractDelay++;
|
|
|
|
if (mRunAwayRefractDelay == 240) {
|
|
startRefract(50);
|
|
mRunAwayRefractDelay = 0;
|
|
}
|
|
|
|
if (rs::updateJudgeAndResult(mJudgeNormalFall)) {
|
|
al::setNerve(this, &NrvGamane.Fall);
|
|
return;
|
|
}
|
|
|
|
if (al::calcDistanceH(this, al::getPlayerActor(this, 0)) > 1500.0f)
|
|
al::setNerve(this, &NrvGamane.Wait);
|
|
}
|
|
|
|
void Gamane::exeFall() {
|
|
if (al::isFirstStep(this)) {
|
|
al::startAction(this, "Fall");
|
|
al::invalidateClipping(this);
|
|
}
|
|
|
|
al::addVelocityToGravity(this, 0.7f);
|
|
al::scaleVelocity(this, 0.99f);
|
|
|
|
if (al::isOnGround(this, 0)) {
|
|
al::reboundVelocityFromCollision(this, 0.0f, 0.0f, 1.0f);
|
|
al::setNerve(this, &Land);
|
|
al::startHitReaction(this, "着地");
|
|
}
|
|
}
|
|
|
|
void Gamane::exeLand() {
|
|
if (al::isFirstStep(this)) {
|
|
al::startAction(this, "Land");
|
|
al::setVelocityZero(this);
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (al::isActionEnd(this))
|
|
al::setNerve(this, &NrvGamane.Wait);
|
|
}
|
|
|
|
void Gamane::exeSwoon() {
|
|
if (al::isFirstStep(this)) {
|
|
sead::Vector3f velocity = sead::Vector3f(0.0f, 25.0f, 0.0f);
|
|
sead::Vector3f frontDir = sead::Vector3f::ez;
|
|
al::calcFrontDir(&frontDir, this);
|
|
velocity += frontDir * 0.0f;
|
|
al::setVelocity(this, velocity);
|
|
mIsKeepSwoon = false;
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (al::updateNerveState(this) && !mIsKeepSwoon) {
|
|
if (al::calcDistanceH(this, al::getPlayerActor(this, 0)) < 1000.0f)
|
|
al::setNerve(this, &NrvGamane.Runaway);
|
|
else
|
|
al::setNerve(this, &NrvGamane.Wait);
|
|
}
|
|
}
|
|
|
|
void Gamane::exeHackStart() {
|
|
if (mStateHackStart->isHackStart() && mStateHackStart->calcHackStartNerveRate() == 0.0f) {
|
|
al::startVisAnim(this, "CapOn");
|
|
al::startMtpAnim(this, "CapOn");
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (al::updateNerveState(this)) {
|
|
al::startVisAnim(this, "On");
|
|
al::setNerve(this, &NrvGamane.Hack);
|
|
}
|
|
}
|
|
|
|
void Gamane::exeHack() {
|
|
al::updateNerveState(this);
|
|
if (mCoinsLeft > 0 && mHackCoinAppearCounter == 0) {
|
|
rs::tryAppearMultiCoinFromObj(this, al::getHitSensor(this, "Body"), 0, 150.0f);
|
|
mCoinsLeft--;
|
|
}
|
|
|
|
// NOTE: this is only one increment, as post-incrementing is used
|
|
mHackCoinAppearCounter = al::wrapValue(mHackCoinAppearCounter++ + 1, 6);
|
|
}
|
|
|
|
void Gamane::exeTrampled() {
|
|
if (al::isFirstStep(this)) {
|
|
al::setVelocityZero(this);
|
|
al::startAction(this, "Trampled");
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (al::isActionEnd(this))
|
|
al::setNerve(this, &NrvGamane.Runaway);
|
|
}
|
|
|
|
void Gamane::exePressDown() {
|
|
if (al::isFirstStep(this)) {
|
|
al::startAction(this, "PressDown");
|
|
al::invalidateClipping(this);
|
|
endRefract(50);
|
|
}
|
|
|
|
updateMovement();
|
|
|
|
if (al::isActionEnd(this)) {
|
|
al::startHitReaction(this, "死亡");
|
|
al::appearItemTiming(this, "倒す");
|
|
kill();
|
|
}
|
|
}
|
|
|
|
void Gamane::exeBlowDown() {
|
|
if (al::isFirstStep(this))
|
|
endRefract(50);
|
|
|
|
if (al::updateNerveState(this)) {
|
|
al::startHitReaction(this, "死亡");
|
|
al::appearItemTiming(this, "倒す");
|
|
kill();
|
|
}
|
|
}
|