//------------------------------------------------------------------------- /* Copyright (C) 1997, 2005 - 3D Realms Entertainment This file is part of Shadow Warrior version 1.2 Shadow Warrior is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. Original Source: 1997 - Frank Maddin and Jim Norwood Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms */ //------------------------------------------------------------------------- #include "ns.h" // Added Ninja Sliced fix // Fixed Ninja sliced dead and rotation // Added s_NinjaDieSlicedHack[] // #include "build.h" #include "names2.h" #include "panel.h" #include "misc.h" #include "tags.h" #include "weapon.h" #include "sprite.h" #include "gamefuncs.h" #include "ai.h" BEGIN_SW_NS extern int jump_grav; extern STATE s_DebrisNinja[]; extern STATE s_DebrisRat[]; extern STATE s_DebrisCrab[]; extern STATE s_DebrisStarFish[]; extern STATE s_NinjaDieSliced[]; extern STATE s_NinjaDieSlicedHack[]; extern STATE* sg_NinjaGrabThroat[]; //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoScaleSprite(DSWActor* actor) { int scale_value; if (actor->user.scale_speed) { actor->user.scale_value += actor->user.scale_speed * ACTORMOVETICS; scale_value = actor->user.scale_value >> 8; if (actor->user.scale_speed > 0) { if (scale_value > actor->user.scale_tgt) actor->user.scale_speed = 0; else actor->spr.scale = DVector2(scale_value * REPEAT_SCALE, scale_value * REPEAT_SCALE); } else { if (scale_value < actor->user.scale_tgt) actor->user.scale_speed = 0; else actor->spr.scale = DVector2(scale_value * REPEAT_SCALE, scale_value * REPEAT_SCALE); } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorDie(DSWActor* actor, DSWActor* weapActor, int meansofdeath) { change_actor_stat(actor, STAT_DEAD_ACTOR); actor->user.Flags |= (SPR_DEAD); actor->user.Flags &= ~(SPR_FALLING | SPR_JUMPING); actor->user.floor_dist = (40); // test for gibable dead bodies actor->spr.extra |= (SPRX_BREAKABLE); actor->spr.cstat |= (CSTAT_SPRITE_BREAKABLE); if (weapActor == nullptr) { // killed by one of these non-sprites switch (meansofdeath) { case WPN_NM_LAVA: actor->ChangeStateEnd(); break; case WPN_NM_SECTOR_SQUISH: actor->ChangeStateEnd(); break; } return 0; } if (!weapActor->hasU()) return 0; // killed by one of these sprites switch (weapActor->user.ID) { // Coolie actually explodes himself // he is the Sprite AND weapon case COOLIE_RUN_R0: actor->ChangeStateEnd(); actor->vel.X *= 2; actor->clearActionFunc(); actor->spr.Angles.Yaw += DAngle180; break; case NINJA_RUN_R0: if (actor->user.ID == NINJA_RUN_R0) // Cut in half! { if (weapActor->user.WeaponNum != WPN_FIST) { if (sw_ninjahack) SpawnBlood(actor, actor); InitPlasmaFountain(weapActor, actor); InitPlasmaFountain(weapActor, actor); PlaySound(DIGI_NINJAINHALF, actor, v3df_none); if (sw_ninjahack) ChangeState(actor, &s_NinjaDieSlicedHack[5]); else ChangeState(actor, &s_NinjaDieSliced[0]); } else { if (RandomRange(1000) > 500) { InitPlasmaFountain(weapActor, actor); } actor->ChangeStateEnd(); actor->clearActionFunc(); actor->vel.X = 12.5 + RandomRangeF(12.5); actor->user.jump_speed = -200 - RandomRange(250); DoActorBeginJump(actor); actor->spr.Angles.Yaw = weapActor->spr.Angles.Yaw; } } else { // test for gibable dead bodies if (RandomRange(1000) > 500) actor->spr.cstat |= (CSTAT_SPRITE_YFLIP); actor->ChangeStateEnd(); actor->vel.X = 0; actor->user.jump_speed = 0; DoActorBeginJump(actor); } actor->user.__legacyState.RotNum = 0; actor->clearActionFunc(); if (!sw_ninjahack) actor->spr.Angles.Yaw = weapActor->spr.Angles.Yaw; break; case COOLG_RUN_R0: case SKEL_RUN_R0: case RIPPER_RUN_R0: case RIPPER2_RUN_R0: case EEL_RUN_R0: case STAR1: case SUMO_RUN_R0: actor->ChangeStateEnd(); break; case UZI_SMOKE: if (RandomRange(1000) > 500) actor->spr.cstat |= (CSTAT_SPRITE_YFLIP); actor->ChangeStateEnd(); // Rippers still gotta jump or they fall off walls weird if (actor->user.ID == RIPPER_RUN_R0 || actor->user.ID == RIPPER2_RUN_R0) { actor->vel.X *= 2; actor->user.jump_speed = -100 - RandomRange(250); DoActorBeginJump(actor); } else { actor->vel.X = 0; actor->user.jump_speed = -10 - RandomRange(25); DoActorBeginJump(actor); } actor->clearActionFunc(); // Get angle to player actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos - actor->spr.pos.Y).Angle() + DAngle180; break; case UZI_SMOKE+1: // Shotgun if (RandomRange(1000) > 500) actor->spr.cstat |= (CSTAT_SPRITE_YFLIP); actor->ChangeStateEnd(); // Rippers still gotta jump or they fall off walls weird if (actor->user.ID == RIPPER_RUN_R0 || actor->user.ID == RIPPER2_RUN_R0) { actor->vel.X = (75./16.) + RandomRangeF(6.25); actor->user.jump_speed = -100 - RandomRange(150); } else { actor->vel.X = 6.25 + RandomRangeF(12.5); actor->user.jump_speed = -100 - RandomRange(250); } DoActorBeginJump(actor); actor->clearActionFunc(); // Get angle to player actor->spr.Angles.Yaw = (actor->user.targetActor->spr.pos - actor->spr.pos).Angle() + DAngle180; break; default: switch (actor->user.ID) { case SKULL_R0: case BETTY_R0: actor->ChangeStateEnd(); break; default: if (RandomRange(1000) > 700) { InitPlasmaFountain(weapActor, actor); } if (RandomRange(1000) > 500) actor->spr.cstat |= (CSTAT_SPRITE_YFLIP); actor->ChangeStateEnd(); actor->clearActionFunc(); actor->vel.X = 18.75 + RandomRangeF(25); actor->user.jump_speed = -300 - RandomRange(350); DoActorBeginJump(actor); actor->spr.Angles.Yaw = weapActor->spr.Angles.Yaw; break; } break; } // These are too big to flip upside down switch (actor->user.ID) { case RIPPER2_RUN_R0: case COOLIE_RUN_R0: case SUMO_RUN_R0: case ZILLA_RUN_R0: actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP); break; } actor->user.ID = 0; return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- void DoDebrisCurrent(DSWActor* actor) { int nx, ny; auto sectp = actor->sector(); double spd = sectp->speed / 64.0; auto vect = sectp->angle.ToVector() * spd; Collision ret = move_sprite(actor, DVector3(vect, 0), actor->user.ceiling_dist, actor->user.floor_dist, 0, ACTORMOVETICS); // attempt to move away from wall if (ret.type != kHitNone) { DAngle rang = RandomAngle(); vect = (sectp->angle + rang).ToVector() * spd; move_sprite(actor, DVector3(vect, 0), actor->user.ceiling_dist, actor->user.floor_dist, 0, ACTORMOVETICS); } actor->spr.pos.Z = actor->user.loz; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorSectorDamage(DSWActor* actor) { sectortype* sectp = actor->sector(); if (actor->user.Health <= 0) return false; if (sectp->hasU() && sectp->damage) { if ((sectp->flags & SECTFU_DAMAGE_ABOVE_SECTOR)) { if ((actor->user.DamageTics -= synctics) < 0) { actor->user.DamageTics = 60; actor->user.Health -= sectp->damage; if (actor->user.Health <= 0) { UpdateSinglePlayKills(actor); DoActorDie(actor, nullptr, WPN_NM_LAVA); return true; } } } else if (ActorZOfBottom(actor) >= sectp->floorz) { if ((actor->user.DamageTics -= synctics) < 0) { actor->user.DamageTics = 60; actor->user.Health -= sectp->damage; if (actor->user.Health <= 0) { UpdateSinglePlayKills(actor); DoActorDie(actor, nullptr, WPN_NM_LAVA); return true; } } } } // note that most squishing is done in vator.c if (actor->user.lo_sectp && actor->user.hi_sectp && abs(actor->user.loz - actor->user.hiz) < (ActorSizeZ(actor) * 0.5)) { actor->user.Health = 0; if (SpawnShrap(actor, nullptr, WPN_NM_SECTOR_SQUISH)) { UpdateSinglePlayKills(actor); SetSuicide(actor); } else { ASSERT(true == false); //DoActorDie(actor, nullptr, WPN_NM_SECTOR_SQUISH); } return true; } return false; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- bool move_debris(DSWActor* actor, const DVector2& change) { actor->user.coll = move_sprite(actor, DVector3(change, 0), actor->user.ceiling_dist, actor->user.floor_dist, 0, ACTORMOVETICS); return actor->user.coll.type == kHitNone; } //--------------------------------------------------------------------------- // // !AIC - Supposed to allow floating of DEBRIS (dead bodies, flotsam, jetsam). Or if water has // current move with the current. // //--------------------------------------------------------------------------- int DoActorDebris(DSWActor* actor) { sectortype* sectp = actor->sector(); // This was move from DoActorDie so actor's can't be walked through until they are on the floor actor->spr.cstat &= ~(CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN); // Don't let some actors float switch (actor->user.ID) { case HORNET_RUN_R0: case BUNNY_RUN_R0: KillActor(actor); return 0; case ZILLA_RUN_R0: calcSlope(actor->sector(), actor->spr.pos.X, actor->spr.pos.Y, &actor->user.hiz, &actor->user.loz); actor->user.lo_sectp = actor->sector(); actor->user.hi_sectp = actor->sector(); actor->user.lowActor = nullptr; actor->user.highActor = nullptr; break; } if ((sectp->extra & SECTFX_SINK)) { if ((sectp->extra & SECTFX_CURRENT)) { DoDebrisCurrent(actor); } else { // todo: check correctness DVector2 nvec = ACTORMOVETICS * maptoworld * actor->spr.Angles.Yaw.ToVector(); if (!move_debris(actor, nvec)) { actor->spr.Angles.Yaw = RandomAngle(); } } if (actor->sector()->hasU() && FixedToInt(actor->sector()->depth_fixed) > 10) // JBF: added null check { actor->user.WaitTics = (actor->user.WaitTics + (ACTORMOVETICS << 3)) & 1023; actor->spr.pos.Z = actor->user.loz - 2 * BobVal(actor->user.WaitTics); } } else { actor->spr.pos.Z = actor->user.loz; } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFireFly(DSWActor* actor) { actor->clipdist = 16; if (!move_actor(actor, DVector3(actor->spr.Angles.Yaw.ToVector() * (0.25 * ACTORMOVETICS), 0))) { actor->spr.Angles.Yaw += DAngle180; } actor->user.WaitTics = (actor->user.WaitTics + (ACTORMOVETICS << 1)) & 2047; actor->spr.pos.Z = actor->user.pos.Z + 32 * BobVal(actor->user.WaitTics); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoGenerateSewerDebris(DSWActor* actor) { static STATE* Debris[] = { s_DebrisNinja, s_DebrisRat, s_DebrisCrab, s_DebrisStarFish }; actor->user.Tics -= ACTORMOVETICS; if (actor->user.Tics <= 0) { actor->user.Tics = actor->user.WaitTics; auto spawned = SpawnActor(STAT_DEAD_ACTOR, 0, Debris[RANDOM_P2(4<<8)>>8], actor->sector(), actor->spr.pos, actor->spr.Angles.Yaw, 12.5); SetOwner(actor, spawned); } return 0; } //--------------------------------------------------------------------------- // // !AIC - Tries to keep actors correctly on the floor. More that a bit messy. // //--------------------------------------------------------------------------- void KeepActorOnFloor(DSWActor* actor) { sectortype* sectp; int depth; sectp = actor->sector(); actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP); // If upside down, reset it if (actor->user.Flags & (SPR_JUMPING | SPR_FALLING)) return; if (actor->user.lo_sectp && actor->user.lo_sectp->hasU()) depth = FixedToInt(actor->user.lo_sectp->depth_fixed); else depth = 0; if ((sectp->extra & SECTFX_SINK) && depth > 35 && actor->hasState(NAME_Swim)) { if (actor->user.Flags & (SPR_SWIMMING)) { if (!actor->checkStateGroup(NAME_Run) && !actor->checkStateGroup(NAME_Swim) && !actor->checkStateGroup(NAME_Stand)) { // was swimming but have now stopped actor->user.Flags &= ~(SPR_SWIMMING); actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER); actor->spr.pos.Z = actor->user.oz = actor->user.loz; return; } if (actor->checkStateGroup(NAME_Run)) { actor->setStateGroup(NAME_Swim); } // are swimming actor->spr.pos.Z = actor->user.oz = actor->user.loz - depth; } else { // only start swimming if you are running if (actor->checkStateGroup(NAME_Run) || actor->checkStateGroup(NAME_Swim)) { actor->setStateGroup(NAME_Swim); actor->spr.pos.Z = actor->user.oz = actor->user.loz - depth; actor->user.Flags |= (SPR_SWIMMING); actor->spr.cstat |= (CSTAT_SPRITE_YCENTER); } else { actor->user.Flags &= ~(SPR_SWIMMING); actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER); actor->spr.pos.Z = actor->user.oz = actor->user.loz; } } return; } // NOT in a swimming situation actor->user.Flags &= ~(SPR_SWIMMING); actor->spr.cstat &= ~(CSTAT_SPRITE_YCENTER); #if 1 if (actor->user.Flags & (SPR_MOVED)) { actor->spr.pos.Z = actor->user.oz = actor->user.loz; } else { double ceilz, florz; Collision ctrash, ftrash; FAFgetzrangepoint(actor->spr.pos, actor->sector(),&ceilz, &ctrash, &florz, &ftrash); actor->spr.pos.Z = actor->user.oz = florz; } #endif } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorBeginSlide(DSWActor* actor, DAngle ang, double vel) { actor->user.Flags |= (SPR_SLIDING); actor->user.slide_ang = ang; actor->user.slide_vel = vel; actor->user.slide_dec = 5/16.; //DoActorSlide(actor); return 0; } //--------------------------------------------------------------------------- // // !AIC - Sliding can occur in different directions from movement of the actor. // Has its own set of variables // //--------------------------------------------------------------------------- int DoActorSlide(DSWActor* actor) { auto vec = actor->user.slide_ang.ToVector() * actor->user.slide_vel; if (!move_actor(actor, DVector3(vec, 0))) { actor->user.Flags &= ~(SPR_SLIDING); return false; } actor->user.slide_vel -= actor->user.slide_dec * ACTORMOVETICS; if (actor->user.slide_vel < 1.25) { actor->user.Flags &= ~(SPR_SLIDING); } return true; } //--------------------------------------------------------------------------- // // !AIC - Actor jumping and falling // //--------------------------------------------------------------------------- int DoActorBeginJump(DSWActor* actor) { actor->user.Flags |= (SPR_JUMPING); actor->user.Flags &= ~(SPR_FALLING); // actor->user.jump_speed = should be set before calling // set up individual actor jump gravity actor->user.jump_grav = ACTOR_GRAVITY; // Change sprites state to jumping if (actor->user.Flags & (SPR_DEAD)) actor->setStateGroup(NAME_DeathJump); else actor->setStateGroup(NAME_Jump); actor->user.__legacyState.StateFallOverride = nullptr; //DO NOT CALL DoActorJump! DoActorStopFall can cause an infinite loop and //stack overflow if it is called. return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorJump(DSWActor* actor) { int jump_adj; // precalculate jump value to adjust jump speed by jump_adj = actor->user.jump_grav * ACTORMOVETICS; // adjust jump speed by gravity - if jump speed greater than 0 player // have started falling if ((actor->user.jump_speed += jump_adj) > 0) { // Start falling DoActorBeginFall(actor); return 0; } // adjust height by jump speed actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR; // if player gets to close the ceiling while jumping auto tex = TexMan.GetGameTexture(actor->spr.spritetexture()); double minh = actor->user.hiz + tex->GetDisplayHeight(); if (actor->spr.pos.Z < minh) { // put player at the ceiling actor->spr.pos.Z = minh; // reverse your speed to falling actor->user.jump_speed = -actor->user.jump_speed; // Change sprites state to falling DoActorBeginFall(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorBeginFall(DSWActor* actor) { actor->user.Flags |= (SPR_FALLING); actor->user.Flags &= ~(SPR_JUMPING); actor->user.jump_grav = ACTOR_GRAVITY; // Change sprites state to falling if (actor->user.Flags & (SPR_DEAD)) { actor->setStateGroup(NAME_DeathFall); } else actor->setStateGroup(NAME_Fall); if (actor->user.__legacyState.StateFallOverride) { NewStateGroup(actor, actor->user.__legacyState.StateFallOverride); } DoActorFall(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorFall(DSWActor* actor) { // adjust jump speed by gravity actor->user.jump_speed += actor->user.jump_grav * ACTORMOVETICS; // adjust player height by jump speed actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR; // Stick like glue when you hit the ground if (actor->spr.pos.Z > actor->user.loz) { DoActorStopFall(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorStopFall(DSWActor* actor) { actor->spr.pos.Z = actor->user.loz; actor->user.Flags &= ~(SPR_FALLING | SPR_JUMPING); actor->spr.cstat &= ~(CSTAT_SPRITE_YFLIP); // don't stand on face or wall sprites - jump again if (actor->user.lowActor && !(actor->user.lowActor->spr.cstat & CSTAT_SPRITE_ALIGNMENT_FLOOR)) { actor->spr.Angles.Yaw += DAngle180 + RandomAngle(DAngle90); actor->user.jump_speed = -350; DoActorBeginJump(actor); return 0; } // Change sprites state to running if (actor->user.__legacyState.ActorActionSet) { if (actor->user.Flags & (SPR_DEAD)) { actor->setStateGroup(NAME_Dead); PlaySound(DIGI_ACTORBODYFALL1, actor, v3df_none); } else { PlaySound(DIGI_ACTORHITGROUND, actor, v3df_none); actor->setStateGroup(NAME_Run); if ((actor->user.track >= 0) && (actor->user.jump_speed) > 800 && (actor->hasState(NAME_Sit))) { actor->user.WaitTics = 80; actor->setStateGroup(NAME_Sit); } } } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoActorDeathMove(DSWActor* actor) { if (actor->user.Flags & (SPR_JUMPING | SPR_FALLING)) { if (actor->user.Flags & (SPR_JUMPING)) DoActorJump(actor); else DoActorFall(actor); } actor->clipdist = 12; move_actor(actor, DVector3(actor->spr.Angles.Yaw.ToVector() * actor->vel.X, 0)); // only fall on top of floor sprite or sector DoFindGroundPoint(actor); return 0; } //--------------------------------------------------------------------------- // // !AIC - Jumping a falling for shrapnel and other stuff, not actors. // //--------------------------------------------------------------------------- int DoBeginJump(DSWActor* actor) { actor->user.Flags |= (SPR_JUMPING); actor->user.Flags &= ~(SPR_FALLING); // set up individual actor jump gravity actor->user.jump_grav = ACTOR_GRAVITY; DoJump(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoJump(DSWActor* actor) { int jump_adj; // precalculate jump value to adjust jump speed by jump_adj = actor->user.jump_grav * ACTORMOVETICS; // adjust jump speed by gravity - if jump speed greater than 0 player // have started falling if ((actor->user.jump_speed += jump_adj) > 0) { // Start falling DoBeginFall(actor); return 0; } // adjust height by jump speed actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR; // if player gets to close the ceiling while jumping auto tex = TexMan.GetGameTexture(actor->spr.spritetexture()); double minh = actor->user.hiz + tex->GetDisplayHeight(); if (actor->spr.pos.Z < minh) { // put player at the ceiling actor->spr.pos.Z = minh; // reverse your speed to falling actor->user.jump_speed = -actor->user.jump_speed; // Change sprites state to falling DoBeginFall(actor); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoBeginFall(DSWActor* actor) { actor->user.Flags |= (SPR_FALLING); actor->user.Flags &= ~(SPR_JUMPING); actor->user.jump_grav = ACTOR_GRAVITY; DoFall(actor); return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- int DoFall(DSWActor* actor) { // adjust jump speed by gravity actor->user.jump_speed += actor->user.jump_grav * ACTORMOVETICS; // adjust player height by jump speed actor->spr.pos.Z += actor->user.jump_speed * ACTORMOVETICS * JUMP_FACTOR; // Stick like glue when you hit the ground if (actor->spr.pos.Z > actor->user.loz - actor->user.floor_dist) { actor->spr.pos.Z = actor->user.loz - actor->user.floor_dist; actor->user.Flags &= ~(SPR_FALLING); } return 0; } //--------------------------------------------------------------------------- // // // //--------------------------------------------------------------------------- // helpers void DSWActor::ChangeStateEnd() { ChangeState(this, user.__legacyState.StateEnd); user.__legacyState.RotNum = 0; } Personality* DSWActor::getPersonality() { return nullptr; // not implemented yet. } static STATE** getLegacyState(ACTOR_ACTION_SET* a, FName label, int subl) { if (label == NAME_Run) { return a->Run; } if (label == NAME_Swim) { return a->Swim; } if (label == NAME_DeathJump) { return a->DeathJump; } if (label == NAME_Jump) { return a->Jump; } if (label == NAME_DeathFall) { return a->DeathFall; } if (label == NAME_Fall) { return a->Fall; } if (label == NAME_Dead) { return a->Dead; } if (label == NAME_Sit) { return a->Sit; } if (label == NAME_Stand) { return a->Stand; } if (label == NAME_Death1) { return a->Death1; } if (label == NAME_Death2) { return a->Death2; } if (label == NAME_Duck) { return a->Duck; } if (label == NAME_Rise) { return a->Rise; } if (label == NAME_Fly) { return a->Fly; } if (label == NAME_Crawl) { return a->Crawl; } if (label == NAME_Pain) { return a->Pain; } if (label == NAME_Climb) { return a->Climb; } if (label == NAME_Special) { return a->Special[1]; // special[0] is never used anywhere } if (label == NAME_CloseAttack) { return a->CloseAttack[subl]; } if (label == NAME_Attack) { return a->Attack[subl]; } return nullptr; } void DSWActor::setStateGroup(FName label, int subl) { auto a = user.__legacyState.ActorActionSet; if (a) NewStateGroup(this, getLegacyState(a, label, subl)); } bool DSWActor::checkStateGroup(FName label, int subl) { auto a = user.__legacyState.ActorActionSet; if (!a) return false; return user.__legacyState.Rot == getLegacyState(a, label, subl); } bool DSWActor::hasState(FName label, int subl) { auto a = user.__legacyState.ActorActionSet; if (!a) return false; return getLegacyState(a, label, subl) != nullptr; } void DSWActor::setActionDecide() { user.ActorActionFunc = AF(DoActorDecide); } void DSWActor::callAction() { callFunction(user.ActorActionFunc); } void DSWActor::callStateAction() { if (user.__legacyState.State && user.__legacyState.State->Animator) callFunction(*user.__legacyState.State->Animator); } int DSWActor::callFunction(VMFunction* func) { int ret = 0; if (func) { VMValue param[] = { this }; VMReturn r(&ret); VMCall(func, param, 1, &r, 1); } return ret; } END_SW_NS