diff --git a/Makefile b/Makefile index 0b1bbe98..b8e45925 100644 --- a/Makefile +++ b/Makefile @@ -921,6 +921,7 @@ GAME_OBJS_ = \ src/game/monster/soldier/soldier.o \ src/game/monster/stalker/stalker.o \ src/game/monster/supertank/supertank.o \ + src/game/monster/shambler/shambler.o \ src/game/monster/tank/tank.o \ src/game/monster/turret/turret.o \ src/game/monster/widow/widow2.o \ diff --git a/README.md b/README.md index b5cf619d..95173663 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,10 @@ State: * q64/outpost: no known issies * mguhub: loaded, transparent walls and broken logic for surface fall in next maps +Monsters: + * incorrect weapon effect for Shambler + * incorrect dead animation for Arachnoid + Goals (none of it finished): * Single player support, * BSPX DECOUPLEDLM light map support (base1), diff --git a/src/game/g_spawn.c b/src/game/g_spawn.c index cf9561b3..63a4cad2 100644 --- a/src/game/g_spawn.c +++ b/src/game/g_spawn.c @@ -162,6 +162,7 @@ void SP_turret_breach(edict_t *self); void SP_turret_base(edict_t *self); void SP_turret_driver(edict_t *self); +void SP_monster_shambler(edict_t *self); void SP_monster_soldier_hypergun(edict_t *self); void SP_monster_soldier_lasergun(edict_t *self); void SP_monster_soldier_ripper(edict_t *self); @@ -352,6 +353,7 @@ static spawn_t spawns[] = { {"monster_makron", SP_monster_makron}, {"monster_jorg", SP_monster_jorg}, {"monster_commander_body", SP_monster_commander_body}, + {"monster_shambler", SP_monster_shambler}, {"monster_soldier_hypergun", SP_monster_soldier_hypergun}, {"monster_soldier_lasergun", SP_monster_soldier_lasergun}, {"monster_soldier_ripper", SP_monster_soldier_ripper}, diff --git a/src/game/monster/guardian/guardian.c b/src/game/monster/guardian/guardian.c index 37f17714..dd97fbf0 100644 --- a/src/game/monster/guardian/guardian.c +++ b/src/game/monster/guardian/guardian.c @@ -305,7 +305,8 @@ mmove_t guardian_atk1_out = guardian_run }; -void guardian_atk1_finish(edict_t *self) +void +guardian_atk1_finish(edict_t *self) { if (!self) { @@ -315,12 +316,14 @@ void guardian_atk1_finish(edict_t *self) self->monsterinfo.currentmove = &guardian_atk1_out; } -void guardian_atk1_charge(edict_t *self) +void +guardian_atk1_charge(edict_t *self) { gi.sound(self, CHAN_WEAPON, sound_charge, 1.f, ATTN_NORM, 0.f); } -void guardian_fire_blaster(edict_t *self) +void +guardian_fire_blaster(edict_t *self) { vec3_t forward, right, target; vec3_t start; @@ -426,7 +429,8 @@ mmove_t guardian_move_atk2_out = guardian_run }; -void guardian_atk2_out(edict_t *self) +void +guardian_atk2_out(edict_t *self) { if (!self) { @@ -443,7 +447,8 @@ static vec3_t laser_positions[] = { { 112.0f, -62.f, 60.f } }; -void guardian_laser_fire(edict_t *self) +void +guardian_laser_fire(edict_t *self) { vec3_t forward, right, up; vec3_t tempang, start; diff --git a/src/game/monster/shambler/shambler.c b/src/game/monster/shambler/shambler.c new file mode 100644 index 00000000..c79bd145 --- /dev/null +++ b/src/game/monster/shambler/shambler.c @@ -0,0 +1,719 @@ +/* + * Copyright (C) 1997-2001 Id Software, Inc. + * Copyright (c) ZeniMax Media Inc. + * + * This program 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., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * ======================================================================= + * + * SHAMBLER + * + * ======================================================================= + */ + +#include "../../header/local.h" +#include "shambler.h" + +static int sound_pain; +static int sound_idle; +static int sound_die; +static int sound_sight; +static int sound_windup; +static int sound_melee1; +static int sound_melee2; +static int sound_smack; +static int sound_boom; + +// +// misc +// + +void +shambler_sight(edict_t* self, edict_t* other) +{ + gi.sound(self, CHAN_VOICE, sound_sight, 1, ATTN_NORM, 0); +} + +static vec3_t lightning_left_hand[] = { + { 44, 36, 25}, + { 10, 44, 57}, + { -1, 40, 70}, + { -10, 34, 75}, + { 7.4f, 24, 89 } +}; + +static vec3_t lightning_right_hand[] = { + { 28, -38, 25}, + { 31, -7, 70}, + { 20, 0, 80}, + { 16, 1.2f, 81}, + { 27, -11, 83 } +}; + +void +shambler_lightning_update(edict_t *self) +{ + edict_t *lightning; + vec3_t f, r; + + if (self->s.frame >= FRAME_magic01 + sizeof(lightning_left_hand) / sizeof(*lightning_left_hand)) + { + return; + } + + lightning = G_Spawn(); + lightning->s.modelindex = gi.modelindex("models/proj/lightning/tris.md2"); + lightning->s.renderfx |= RF_BEAM; + lightning->owner = self; + + AngleVectors(self->s.angles, f, r, NULL); + G_ProjectSource(self->s.origin, lightning_left_hand[self->s.frame - FRAME_magic01], f, r, lightning->s.origin); + G_ProjectSource(self->s.origin, lightning_right_hand[self->s.frame - FRAME_magic01], f, r, lightning->s.old_origin); + gi.linkentity(lightning); +} + +void shambler_windup(edict_t* self) +{ + gi.sound(self, CHAN_WEAPON, sound_windup, 1, ATTN_NORM, 0); + + shambler_lightning_update(self); +} + +void +shambler_idle(edict_t* self) +{ + gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); +} + +void +shambler_maybe_idle(edict_t* self) +{ + if (random() > 0.8) + { + gi.sound(self, CHAN_VOICE, sound_idle, 1, ATTN_IDLE, 0); + } +} + +// +// stand +// + +static mframe_t shambler_frames_stand[] = +{ + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL}, + {ai_stand, 0, NULL} +}; + +mmove_t shambler_move_stand = +{ + FRAME_stand01, + FRAME_stand17, + shambler_frames_stand, + NULL +}; + +void +shambler_stand(edict_t* self) +{ + if (!self) + { + return; + } + + self->monsterinfo.currentmove = &shambler_move_stand; +} + +// +// walk +// + +void shambler_walk(edict_t* self); + +static mframe_t shambler_frames_walk[] = +{ + {ai_walk, 10, NULL}, /* FIXME: add footsteps? */ + {ai_walk, 9, NULL}, + {ai_walk, 9, NULL}, + {ai_walk, 5, NULL}, + {ai_walk, 6, NULL}, + {ai_walk, 12, NULL}, + {ai_walk, 8, NULL}, + {ai_walk, 3, NULL}, + {ai_walk, 13, NULL}, + {ai_walk, 9, NULL}, + {ai_walk, 7, shambler_maybe_idle}, + {ai_walk, 5, NULL}, +}; + +mmove_t shambler_move_walk = +{ + FRAME_walk01, + FRAME_walk12, + shambler_frames_walk, + NULL +}; + +void +shambler_walk(edict_t* self) +{ + if (!self) + { + return; + } + + self->monsterinfo.currentmove = &shambler_move_walk; +} + +// +// run +// + +void shambler_run(edict_t* self); + +static mframe_t shambler_frames_run[] = +{ + {ai_run, 20, NULL}, /* FIXME: add footsteps? */ + {ai_run, 24, NULL}, + {ai_run, 20, NULL}, + {ai_run, 20, NULL}, + {ai_run, 24, NULL}, + {ai_run, 20, shambler_maybe_idle}, +}; + +mmove_t shambler_move_run = +{ + FRAME_run01, + FRAME_run06, + shambler_frames_run, + NULL +}; + +void +shambler_run(edict_t* self) +{ + if (!self) + { + return; + } + + if (self->enemy && self->enemy->client) + { + self->monsterinfo.aiflags |= AI_BRUTAL; + } + else + { + self->monsterinfo.aiflags &= ~AI_BRUTAL; + } + + if (self->monsterinfo.aiflags & AI_STAND_GROUND) + { + self->monsterinfo.currentmove = &shambler_move_stand; + return; + } + + self->monsterinfo.currentmove = &shambler_move_run; +} + +// +// pain +// + +// FIXME: needs halved explosion damage + +static mframe_t shambler_frames_pain[] = { + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, +}; + +mmove_t shambler_move_pain = +{ + FRAME_pain01, + FRAME_pain06, + shambler_frames_pain, + shambler_run +}; + +void +shambler_pain(edict_t *self, edict_t *other /* unused */, + float kick /* unused */, int damage /* unused */) +{ + if (!self) + { + return; + } + + if (level.time < self->timestamp) + { + return; + } + + self->timestamp = level.time + (1.0f / 1000.0f); + gi.sound(self, CHAN_AUTO, sound_pain, 1, ATTN_NORM, 0); + + if (damage <= 30 && random() > 0.2f) + { + return; + } + + /* If hard or nightmare, don't go into pain while attacking */ + if (skill->value >= SKILL_HARDPLUS) + { + if ((self->s.frame >= FRAME_smash01) && (self->s.frame <= FRAME_smash12)) + return; + + if ((self->s.frame >= FRAME_swingl01) && (self->s.frame <= FRAME_swingl09)) + return; + + if ((self->s.frame >= FRAME_swingr01) && (self->s.frame <= FRAME_swingr09)) + return; + } + + if (skill->value == SKILL_HARDPLUS) + { + return; /* no pain anims in nightmare */ + } + + if (level.time < self->pain_debounce_time) + { + return; + } + + self->pain_debounce_time = level.time + 2; + self->monsterinfo.currentmove = &shambler_move_pain; +} + +/* + * attacks + */ + +void +ShamblerSaveLoc(edict_t* self) +{ + /* save for aiming the shot */ + VectorCopy(self->enemy->s.origin, self->pos1); + self->pos1[2] += self->enemy->viewheight; + self->monsterinfo.nextframe = FRAME_magic09; + + gi.sound(self, CHAN_WEAPON, sound_boom, 1, ATTN_NORM, 0); + shambler_lightning_update(self); +} + +void +ShamblerCastLightning(edict_t* self) +{ + vec3_t start; + vec3_t forward, right, target; + vec3_t offset = {0, 0, 60.f}; + + if (!self->enemy) + { + return; + } + + AngleVectors(self->s.angles, forward, right, NULL); + G_ProjectSource(self->s.origin, offset, forward, right, start); + VectorCopy(self->enemy->s.origin, target); + target[2] += self->enemy->viewheight; + for (int i = 0; i < 3; i++) + { + target[i] += (randk() % 10) - 5.f; + } + + /* calc direction to where we targeted */ + VectorSubtract(target, start, forward); + VectorNormalize(forward); + + /* TODO should be tesla effect */ + monster_fire_railgun(self, start, forward, 2, 1000, MZ2_WIDOW_RAIL); +} + +static mframe_t shambler_frames_magic[] = { + {ai_charge, 0, shambler_windup}, + {ai_charge, 0, shambler_lightning_update}, + {ai_charge, 0, shambler_lightning_update}, + {ai_move, 0, shambler_lightning_update}, + {ai_move, 0, shambler_lightning_update}, + {ai_move, 0, ShamblerSaveLoc}, + {ai_move, 0, NULL}, + {ai_charge, 0, NULL}, + {ai_move, 0, ShamblerCastLightning}, + {ai_move, 0, ShamblerCastLightning}, + {ai_move, 0, ShamblerCastLightning}, + {ai_move, 0, NULL}, +}; + +mmove_t shambler_attack_magic = +{ + FRAME_magic01, + FRAME_magic12, + shambler_frames_magic, + shambler_run +}; + +void +shambler_attack(edict_t* self) +{ + if (!self) + { + return; + } + + self->monsterinfo.currentmove = &shambler_attack_magic; +} + +// +// melee +// + +void shambler_melee1(edict_t* self) +{ + gi.sound(self, CHAN_WEAPON, sound_melee1, 1, ATTN_NORM, 0); +} + +void shambler_melee2(edict_t* self) +{ + gi.sound(self, CHAN_WEAPON, sound_melee2, 1, ATTN_NORM, 0); +} + +void sham_swingl9(edict_t* self); +void sham_swingr9(edict_t* self); + +void sham_smash10(edict_t* self) +{ + if (!self->enemy) + return; + + ai_charge(self, 0); + + if (!CanDamage(self->enemy, self)) + return; + + vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 }; + if (fire_hit(self, aim, (110 + (randk() % 10)), 120)) + { + /* Slower attack */ + gi.sound(self, CHAN_WEAPON, sound_smack, 1, ATTN_NORM, 0); + } +}; + +void ShamClaw(edict_t* self) +{ + if (!self->enemy) + return; + + ai_charge(self, 10); + + if (!CanDamage(self->enemy, self)) + return; + + vec3_t aim = { MELEE_DISTANCE, self->mins[0], -4 }; + if (fire_hit(self, aim, (70 + (randk() % 10)), 80)) + { + /* Slower attack */ + gi.sound(self, CHAN_WEAPON, sound_smack, 1, ATTN_NORM, 0); + } +}; + +static mframe_t shambler_frames_smash[] = { + {ai_charge, 2, shambler_melee1}, + {ai_charge, 6}, + {ai_charge, 6}, + {ai_charge, 5}, + {ai_charge, 4}, + {ai_charge, 1}, + {ai_charge, 0}, + {ai_charge, 0}, + {ai_charge, 0}, + {ai_charge, 0, sham_smash10}, + {ai_charge, 5}, + {ai_charge, 4}, +}; + +mmove_t shambler_attack_smash = +{ + FRAME_smash01, + FRAME_smash12, + shambler_frames_smash, + shambler_run +}; + +static mframe_t shambler_frames_swingl[] = { + {ai_charge, 5, shambler_melee1}, + {ai_charge, 3}, + {ai_charge, 7}, + {ai_charge, 3}, + {ai_charge, 7}, + {ai_charge, 9}, + {ai_charge, 5, ShamClaw}, + {ai_charge, 4}, + {ai_charge, 8, sham_swingl9}, +}; + +mmove_t shambler_attack_swingl = +{ + FRAME_swingl01, + FRAME_swingl09, + shambler_frames_swingl, + shambler_run +}; + +static mframe_t shambler_frames_swingr[] = { + {ai_charge, 1, shambler_melee2}, + {ai_charge, 8}, + {ai_charge, 14}, + {ai_charge, 7}, + {ai_charge, 3}, + {ai_charge, 6}, + {ai_charge, 6, ShamClaw}, + {ai_charge, 3}, + {ai_charge, 8, sham_swingr9}, +}; + +mmove_t shambler_attack_swingr = +{ + FRAME_swingr01, + FRAME_swingr09, + shambler_frames_swingr, + shambler_run +}; + +void +sham_swingl9(edict_t* self) +{ + if (!self) + { + return; + } + + ai_charge(self, 8); + + if (self->enemy) + { + float real_enemy_range; + + real_enemy_range = realrange(self, self->enemy); + + if ((randk() % 2) && self->enemy && real_enemy_range < MELEE_DISTANCE) + { + self->monsterinfo.currentmove = &shambler_attack_swingr; + } + } +} + +void +sham_swingr9(edict_t* self) +{ + if (!self) + { + return; + } + + ai_charge(self, 1); + ai_charge(self, 10); + + if (self->enemy) + { + float real_enemy_range; + + real_enemy_range = realrange(self, self->enemy); + + if ((randk() % 2) && self->enemy && real_enemy_range < MELEE_DISTANCE) + { + self->monsterinfo.currentmove = &shambler_attack_swingl; + } + } +} + +void +shambler_melee(edict_t* self) +{ + float chance = random(); + if (chance > 0.6 || self->health == 600) + { + self->monsterinfo.currentmove = &shambler_attack_smash; + } + else if (chance > 0.3) + { + self->monsterinfo.currentmove = &shambler_attack_swingl; + } + else + { + self->monsterinfo.currentmove = &shambler_attack_swingr; + } +} + +// +// death +// + +void +shambler_dead(edict_t* self) +{ + if (!self) + { + return; + } + + VectorSet(self->mins, -16, -16, -24); + VectorSet(self->maxs, 16, 16, -8); + self->movetype = MOVETYPE_TOSS; + self->svflags |= SVF_DEADMONSTER; + self->nextthink = 0; + gi.linkentity(self); +} + +static void +shambler_shrink(edict_t* self) +{ + self->maxs[2] = 0; + self->svflags |= SVF_DEADMONSTER; + gi.linkentity(self); +} + +static mframe_t shambler_frames_death[] = { + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, shambler_shrink}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, + {ai_move, 0, NULL}, /* FIXME: thud? */ +}; + +mmove_t shambler_move_death = +{ + FRAME_death01, + FRAME_death11, + shambler_frames_death, + shambler_dead +}; + +void +shambler_die(edict_t *self, edict_t *inflictor /* unused */, edict_t *attacker /* unused */, + int damage, vec3_t point /* unused */) +{ + /* check for gib */ + if (self->health <= self->gib_health) + { + gi.sound(self, CHAN_VOICE, gi.soundindex("misc/udeath.wav"), 1, ATTN_NORM, 0); + + /* FIXME: better gibs for shambler, shambler head */ + ThrowGib(self, "models/objects/gibs/sm_meat/tris.md2", + damage, GIB_ORGANIC); + ThrowGib(self, "models/objects/gibs/chest/tris.md2", + damage, GIB_ORGANIC); + + ThrowHead(self, "models/objects/gibs/head2/tris.md2", + damage, GIB_ORGANIC); + + self->deadflag = true; + return; + } + + if (self->deadflag) + return; + + // regular death + gi.sound(self, CHAN_VOICE, sound_die, 1, ATTN_NORM, 0); + self->deadflag = true; + self->takedamage = true; + + self->monsterinfo.currentmove = &shambler_move_death; +} + +void SP_monster_shambler(edict_t* self) +{ + if (!self) + { + return; + } + + if (deathmatch->value) + { + G_FreeEdict(self); + return; + } + + self->s.modelindex = gi.modelindex("models/monsters/shambler/tris.md2"); + VectorSet (self->mins, -32, -32, -24); + VectorSet (self->maxs, 32, 32, 64); + self->movetype = MOVETYPE_STEP; + self->solid = SOLID_BBOX; + + gi.modelindex("models/proj/lightning/tris.md2"); + sound_pain = gi.soundindex("shambler/shurt2.wav"); + sound_idle = gi.soundindex("shambler/sidle.wav"); + sound_die = gi.soundindex("shambler/sdeath.wav"); + sound_windup = gi.soundindex("shambler/sattck1.wav"); + sound_melee1 = gi.soundindex("shambler/melee1.wav"); + sound_melee2 = gi.soundindex("shambler/melee2.wav"); + sound_sight = gi.soundindex("shambler/ssight.wav"); + sound_smack = gi.soundindex("shambler/smack.wav"); + sound_boom = gi.soundindex("shambler/sboom.wav"); + + self->health = 600; + self->gib_health = -60; + + self->mass = 500; + + self->pain = shambler_pain; + self->die = shambler_die; + self->monsterinfo.stand = shambler_stand; + self->monsterinfo.walk = shambler_walk; + self->monsterinfo.run = shambler_run; + self->monsterinfo.dodge = NULL; + self->monsterinfo.attack = shambler_attack; + self->monsterinfo.melee = shambler_melee; + self->monsterinfo.sight = shambler_sight; + self->monsterinfo.idle = shambler_idle; + self->monsterinfo.blocked = NULL; + + gi.linkentity(self); + + if (self->spawnflags & 1) + { + self->monsterinfo.aiflags |= AI_IGNORE_SHOTS; + } + + self->monsterinfo.currentmove = &shambler_move_stand; + self->monsterinfo.scale = MODEL_SCALE; + + walkmonster_start(self); +} diff --git a/src/game/monster/shambler/shambler.h b/src/game/monster/shambler/shambler.h new file mode 100644 index 00000000..d4c95b03 --- /dev/null +++ b/src/game/monster/shambler/shambler.h @@ -0,0 +1,124 @@ +/* + * Copyright (C) 1997-2001 Id Software 30 Inc. + * Copyright (c) ZeniMax Media Inc. + * + * This program 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 30 or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful 30 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 30 write to the Free Software + * Foundation 30 Inc. 30 59 Temple Place - Suite 330 30 Boston 30 MA + * 02111-1307 30 USA. + * + * ======================================================================= + * + * Tank and Tank Commander animations. + * + * ======================================================================= + */ + +#define FRAME_stand01 0 +#define FRAME_stand02 1 +#define FRAME_stand03 2 +#define FRAME_stand04 3 +#define FRAME_stand05 4 +#define FRAME_stand06 5 +#define FRAME_stand07 6 +#define FRAME_stand08 7 +#define FRAME_stand09 8 +#define FRAME_stand10 9 +#define FRAME_stand11 10 +#define FRAME_stand12 11 +#define FRAME_stand13 12 +#define FRAME_stand14 13 +#define FRAME_stand15 14 +#define FRAME_stand16 15 +#define FRAME_stand17 16 +#define FRAME_stand18 17 +#define FRAME_walk01 18 +#define FRAME_walk02 19 +#define FRAME_walk03 20 +#define FRAME_walk04 21 +#define FRAME_walk05 22 +#define FRAME_walk06 23 +#define FRAME_walk07 24 +#define FRAME_walk08 25 +#define FRAME_walk09 26 +#define FRAME_walk10 27 +#define FRAME_walk11 28 +#define FRAME_walk12 29 +#define FRAME_run01 30 +#define FRAME_run02 31 +#define FRAME_run03 32 +#define FRAME_run04 33 +#define FRAME_run05 34 +#define FRAME_run06 35 +#define FRAME_smash01 36 +#define FRAME_smash02 37 +#define FRAME_smash03 38 +#define FRAME_smash04 39 +#define FRAME_smash05 40 +#define FRAME_smash06 41 +#define FRAME_smash07 42 +#define FRAME_smash08 43 +#define FRAME_smash09 44 +#define FRAME_smash10 45 +#define FRAME_smash11 46 +#define FRAME_smash12 47 +#define FRAME_swingr01 48 +#define FRAME_swingr02 49 +#define FRAME_swingr03 50 +#define FRAME_swingr04 51 +#define FRAME_swingr05 52 +#define FRAME_swingr06 53 +#define FRAME_swingr07 54 +#define FRAME_swingr08 55 +#define FRAME_swingr09 56 +#define FRAME_swingl01 57 +#define FRAME_swingl02 58 +#define FRAME_swingl03 59 +#define FRAME_swingl04 60 +#define FRAME_swingl05 61 +#define FRAME_swingl06 62 +#define FRAME_swingl07 63 +#define FRAME_swingl08 64 +#define FRAME_swingl09 65 +#define FRAME_magic01 66 +#define FRAME_magic02 67 +#define FRAME_magic03 68 +#define FRAME_magic04 69 +#define FRAME_magic05 70 +#define FRAME_magic06 71 +#define FRAME_magic07 72 +#define FRAME_magic08 73 +#define FRAME_magic09 74 +#define FRAME_magic10 75 +#define FRAME_magic11 76 +#define FRAME_magic12 77 +#define FRAME_pain01 78 +#define FRAME_pain02 79 +#define FRAME_pain03 80 +#define FRAME_pain04 81 +#define FRAME_pain05 82 +#define FRAME_pain06 83 +#define FRAME_death01 84 +#define FRAME_death02 85 +#define FRAME_death03 86 +#define FRAME_death04 87 +#define FRAME_death05 88 +#define FRAME_death06 89 +#define FRAME_death07 90 +#define FRAME_death08 91 +#define FRAME_death09 92 +#define FRAME_death10 93 +#define FRAME_death11 94 + +#define MODEL_SCALE 1.000000