--- /dev/null
+/*
+Generated framegroups file for Dragon
+Used by DarkPlaces to simulate frame groups in DPM models.
+*/
+
+1 101 30 1 // dragon idle
+102 101 30 1 // dragon glide
+203 101 30 1 // dragon fly
+304 26 30 0 // dragon pain1
+330 26 30 0 // dragon pain2
+356 51 30 0 // dragon melee
+407 51 30 0 // dragon fireball
+458 101 30 1 // dragon dying
+559 26 30 0 // dragon dead
--- /dev/null
+/*
+Generated framegroups file for golem
+Used by DarkPlaces to simulate frame groups in DPM models.
+*/
+
+1 21 5 1 // golem idle
+22 61 30 1 // golem run
+83 21 30 1 // golem runangry
+104 36 30 1 // golem melee01
+140 36 30 1 // golem melee02
+176 61 30 1 // golem melee03
+237 61 30 1 // golem melee04
+298 21 30 0 // golem hit01
+319 21 30 0 // golem hit02
+340 21 30 0 // golem hit03
+361 21 30 0 // golem hit04
+382 61 30 0 // golem hithard
+443 101 30 0 // golem enrage
+544 61 30 0 // golem death01
+605 2 30 0 // golem dead01
+607 36 30 0 // golem death02
+643 2 30 0 // golem dead01
+645 46 30 0 // golem deathback
+691 2 30 0 // golem deadback
+693 2 30 0 // golem dead01
+695 2 30 0 // golem dead01
+697 2 30 0 // golem dead01
+699 2 30 0 // golem dead01
+701 2 30 0 // golem dead01
+++ /dev/null
-/*
-Generated framegroups file for mage
-Used by DarkPlaces to simulate frame groups in DPM models.
-*/
-
-1 31 30 1 // mage idle
-32 31 30 1 // mage walk
-63 16 30 1 // mage attack
-79 16 30 1 // mage hit
-95 31 30 0 // mage die
-126 31 60 1 // mage walk
--- /dev/null
+/*
+Generated framegroups file for nanomage
+Used by DarkPlaces to simulate frame groups in DPM models.
+*/
+
+1 61 12 1 // nanomage idle
+62 9 10 1 // nanomage run
+71 71 30 0 // nanomage attack01
+142 43 30 0 // nanomage attack02
+185 61 30 0 // nanomage attack03
+246 101 30 0 // nanomage attack04
+347 11 30 0 // nanomage pain01
+358 16 30 0 // nanomage pain02
+374 31 30 0 // nanomage pain03
+405 86 30 0 // nanomage death01
+491 56 30 0 // nanomage death02
+547 2 1 0 // nanomage dead01
+549 2 1 0 // nanomage dead02
+++ /dev/null
-1 16 10 1 // shambler stand\r18 11 10 1 // shambler walk\r31 5 10 1 // shambler run\r37 11 10 1 // shambler smash\r49 8 10 1 // shambler swing right\r58 8 10 1 // shambler swing left\r67 11 10 1 // shambler magic\r79 5 10 0 // shambler pain\r85 10 10 0 // shambler death
\ No newline at end of file
+++ /dev/null
-//TAG: shambler
-//death sound/monsters/shambler/death 0
-sight sound/monsters/shambler/sight 0
-//ranged sound/monsters/shambler/ranged 0
-//melee sound/monsters/shambler/melee 0
-//pain sound/monsters/shambler/pain 0
-//spawn sound/monsters/shambler/spawn 0
-idle sound/monsters/shambler/idle 2
Used by DarkPlaces to simulate frame groups in DPM models.
*/
-1 60 60 1 // spider idle
-61 41 120 1 // spider walk
-102 24 60 1 // spider attack
-125 10 60 1 // spider attack2
\ No newline at end of file
+1 51 30 0 // spider spiderbite
+52 51 30 0 // spider spiderdeath01
+103 51 30 0 // spider spiderdeath02
+154 11 30 0 // spider spiderfire01
+165 31 30 0 // spider spiderfire02
+196 51 5 1 // spider spideridle
+247 51 30 0 // spider spiderintimidate
+298 11 25 0 // spider spiderpain01
+309 11 25 0 // spider spiderpain02
+320 11 15 0 // spider spiderpain03
+331 101 30 1 // spider spiderwalkforward
+432 101 30 1 // spider spiderwalkforwardright
+533 101 30 1 // spider spiderwalkright
+634 101 30 1 // spider spiderwalkbackright
+735 101 30 1 // spider spiderwalkback
+836 101 30 1 // spider spiderwalkbackleft
+937 101 30 1 // spider spiderwalkleft
+1038 101 30 1 // spider spiderwalkforwardleft
+++ /dev/null
-1 14 10 1 // wizard hover\r16 13 10 1 // wizard fly\r30 12 10 1 // wizard magic attack\r43 3 10 0 // wizard pain\r47 7 10 0 // wizard death
\ No newline at end of file
set g_monster_spider_attack_bite_damage 35
set g_monster_spider_attack_bite_delay 1.5
set g_monster_spider_attack_web_damagetime 7
-set g_monster_spider_attack_web_delay 1.5
+set g_monster_spider_attack_web_delay 3
+set g_monster_spider_attack_web_range 800
set g_monster_spider_attack_web_speed 1300
set g_monster_spider_attack_web_speed_up 150
set g_monster_spider_damageforcescale 0.600000024
set g_monster_wyvern_speed_walk 120
// }}}
// {{{ #5: Shambler
-set g_monster_shambler_attack_claw_damage 60
-set g_monster_shambler_attack_lightning_damage 25
-set g_monster_shambler_attack_lightning_damage_zap 15
-set g_monster_shambler_attack_lightning_force 100
-set g_monster_shambler_attack_lightning_radius 50
-set g_monster_shambler_attack_lightning_radius_zap 250
-set g_monster_shambler_attack_lightning_speed 1000
-set g_monster_shambler_attack_lightning_speed_up 150
-set g_monster_shambler_attack_smash_damage 50
-set g_monster_shambler_attack_smash_range 0
-set g_monster_shambler_damageforcescale 0.100000001
-set g_monster_shambler_health 650
-set g_monster_shambler_speed_run 320
-set g_monster_shambler_speed_stop 300
-set g_monster_shambler_speed_walk 150
+set g_monster_golem_attack_claw_damage 60
+set g_monster_golem_attack_lightning_damage 25
+set g_monster_golem_attack_lightning_damage_zap 15
+set g_monster_golem_attack_lightning_force 100
+set g_monster_golem_attack_lightning_radius 50
+set g_monster_golem_attack_lightning_radius_zap 250
+set g_monster_golem_attack_lightning_speed 1000
+set g_monster_golem_attack_lightning_speed_up 150
+set g_monster_golem_attack_smash_damage 50
+set g_monster_golem_attack_smash_force 100
+set g_monster_golem_attack_smash_range 200
+set g_monster_golem_damageforcescale 0.100000001
+set g_monster_golem_health 650
+set g_monster_golem_speed_run 320
+set g_monster_golem_speed_stop 300
+set g_monster_golem_speed_walk 150
// }}}
// {{{ Misc
set g_monsters_ignoretraces 1
set g_monsters_lineofsight 1
set g_monsters_owners 1
+set g_monsters_playerclip_collisions 0
set g_monsters_teams 1
set g_monsters_score_kill 0
set g_monsters_score_spawned 0
set g_monsters_target_range 2000
set g_monsters_target_infront 0
set g_monsters_target_infront_range 0.3
+set g_monsters_target_infront_2d 1
set g_monsters_attack_range 120
set g_monsters_respawn 1
set g_monsters_respawn_delay 20
.int lodmodelindex0;
.int lodmodelindex1;
.int lodmodelindex2;
-void CSQCPlayer_LOD_Apply(entity this)
+void CSQCPlayer_LOD_Apply(entity this, bool isplayer)
{
+ int detailreduction = ((isplayer) ? autocvar_cl_playerdetailreduction : autocvar_cl_modeldetailreduction);
+
// LOD model loading
if(this.lodmodelindex0 != this.modelindex)
{
}
// apply LOD
- if(autocvar_cl_playerdetailreduction <= 0)
+ if(detailreduction <= 0)
{
- if(autocvar_cl_playerdetailreduction <= -2)
+ if(detailreduction <= -2)
this.modelindex = this.lodmodelindex2;
- else if(autocvar_cl_playerdetailreduction <= -1)
+ else if(detailreduction <= -1)
this.modelindex = this.lodmodelindex1;
else
this.modelindex = this.lodmodelindex0;
}
else
{
- float distance = vlen(this.origin - view_origin);
- float f = (distance * current_viewzoom + 100.0) * autocvar_cl_playerdetailreduction;
+ float distance = vlen(((isplayer) ? this.origin : NearestPointOnBox(this, view_origin)) - view_origin); // TODO: perhaps it should just use NearestPointOnBox all the time, player hitbox can potentially be huge
+ float f = (distance * current_viewzoom + 100.0) * detailreduction;
f *= 1.0 / bound(0.01, view_quality, 1);
if(f > autocvar_cl_loddistance2)
this.modelindex = this.lodmodelindex2;
if(this.isplayermodel) // this checks if it's a player MODEL!
{
CSQCPlayer_ModelAppearance_Apply(this, this.entnum == player_localnum + 1);
- CSQCPlayer_LOD_Apply(this);
+ CSQCPlayer_LOD_Apply(this, true);
if(!isplayer)
{
}
}
}
+ else
+ CSQCPlayer_LOD_Apply(this, false);
CSQCModel_AutoTagIndex_Apply(this);
HANDLE(SEEKER) this.traileffect = EFFECT_SEEKER_TRAIL.m_id; break;
HANDLE(MAGE_SPIKE) this.traileffect = EFFECT_TR_VORESPIKE.m_id; break;
- HANDLE(SHAMBLER_LIGHTNING) this.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
+ HANDLE(GOLEM_LIGHTNING) this.traileffect = EFFECT_TR_NEXUIZPLASMA.m_id; break;
HANDLE(RAPTORBOMB) this.gravity = 1; this.avelocity = '0 0 180'; this.traileffect = EFFECT_Null.m_id; break;
HANDLE(RAPTORBOMBLET) this.gravity = 1; this.avelocity = '0 0 180'; this.traileffect = EFFECT_Null.m_id; break;
this.bouncefactor = WEP_CVAR(mortar, bouncefactor);
this.bouncestop = WEP_CVAR(mortar, bouncestop);
break;
- case PROJECTILE_SHAMBLER_LIGHTNING:
+ case PROJECTILE_GOLEM_LIGHTNING:
this.mins = '-8 -8 -8';
this.maxs = '8 8 8';
this.scale = 2.5;
REGISTER_DEATHTYPE(LAVA, DEATH_SELF_LAVA, DEATH_MURDER_LAVA, "")
REGISTER_DEATHTYPE(MIRRORDAMAGE, DEATH_SELF_BETRAYAL, NULL, "")
REGISTER_DEATHTYPE(MONSTER_MAGE, DEATH_SELF_MON_MAGE, DEATH_MURDER_MONSTER, "monster")
-REGISTER_DEATHTYPE(MONSTER_SHAMBLER_CLAW, DEATH_SELF_MON_SHAMBLER_CLAW, DEATH_MURDER_MONSTER, "monster")
-REGISTER_DEATHTYPE(MONSTER_SHAMBLER_SMASH, DEATH_SELF_MON_SHAMBLER_SMASH, DEATH_MURDER_MONSTER, "monster")
-REGISTER_DEATHTYPE(MONSTER_SHAMBLER_ZAP, DEATH_SELF_MON_SHAMBLER_ZAP, DEATH_MURDER_MONSTER, "monster")
+REGISTER_DEATHTYPE(MONSTER_GOLEM_CLAW, DEATH_SELF_MON_GOLEM_CLAW, DEATH_MURDER_MONSTER, "monster")
+REGISTER_DEATHTYPE(MONSTER_GOLEM_SMASH, DEATH_SELF_MON_GOLEM_SMASH, DEATH_MURDER_MONSTER, "monster")
+REGISTER_DEATHTYPE(MONSTER_GOLEM_ZAP, DEATH_SELF_MON_GOLEM_ZAP, DEATH_MURDER_MONSTER, "monster")
REGISTER_DEATHTYPE(MONSTER_SPIDER, DEATH_SELF_MON_SPIDER, DEATH_MURDER_MONSTER, "monster")
REGISTER_DEATHTYPE(MONSTER_WYVERN, DEATH_SELF_MON_WYVERN, DEATH_MURDER_MONSTER, "monster")
REGISTER_DEATHTYPE(MONSTER_ZOMBIE_JUMP, DEATH_SELF_MON_ZOMBIE_JUMP, DEATH_MURDER_MONSTER, "monster")
FOREACH(Monsters, it != MON_Null,
{
- if((it.spawnflags & MON_FLAG_HIDDEN) || (it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM) ||
- (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1))
+ if((it.spawnflags & MON_FLAG_HIDDEN) || (it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM)
+ || (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1))
continue;
if(autocvar_g_invasion_zombies_only && !(it.spawnflags & MONSTER_TYPE_UNDEAD))
continue;
this.takedamage = DAMAGE_NO;
this.event_damage = func_null;
this.bot_attack = false;
+ this.monster_attack = false;
}
// precache all the models
this.reset = func_breakable_reset;
this.reset(this);
+ if(this.monster_attack)
+ IL_PUSH(g_monster_targets, this);
+
IL_PUSH(g_initforplayer, this);
this.init_for_player = func_breakable_init_for_player;
MODEL(PROJECTILE_SEEKER, "models/tagrocket.md3");
MODEL(PROJECTILE_MAGE_SPIKE, "models/ebomb.mdl");
-MODEL(PROJECTILE_SHAMBLER_LIGHTNING, "models/ebomb.mdl");
+MODEL(PROJECTILE_GOLEM_LIGHTNING, "models/ebomb.mdl");
MODEL(PROJECTILE_RAPTORBOMB, "models/vehicles/clusterbomb.md3");
MODEL(PROJECTILE_RAPTORBOMBLET, "models/vehicles/bomblet.md3");
const int MONSTER_RESPAWN_DEATHPOINT = BIT(4); // re-spawn where we died
const int MONSTER_TYPE_FLY = BIT(5);
const int MONSTER_TYPE_SWIM = BIT(6);
-const int MONSTER_SIZE_BROKEN = BIT(7); // TODO: remove when bad models are replaced
+// bit 7 now unused
const int MON_FLAG_SUPERMONSTER = BIT(8); // incredibly powerful monster
const int MON_FLAG_RANGED = BIT(9); // monster shoots projectiles
const int MON_FLAG_MELEE = BIT(10);
METHOD(Monster, mr_setup, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
/** (SERVER) logic to run every frame */
METHOD(Monster, mr_think, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
+ /** (SERVER) logic to run every frame after the monster has died */
+ METHOD(Monster, mr_deadthink, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
/** (SERVER) called when monster dies */
METHOD(Monster, mr_death, bool(Monster this, entity actor)) { TC(Monster, this); return false; }
/** (SERVER) called when monster is damaged */
// generated file; do not modify
+#include <common/monsters/monster/golem.qc>
#include <common/monsters/monster/mage.qc>
-#include <common/monsters/monster/shambler.qc>
#include <common/monsters/monster/spider.qc>
#include <common/monsters/monster/wyvern.qc>
#include <common/monsters/monster/zombie.qc>
// generated file; do not modify
+#include <common/monsters/monster/golem.qh>
#include <common/monsters/monster/mage.qh>
-#include <common/monsters/monster/shambler.qh>
#include <common/monsters/monster/spider.qh>
#include <common/monsters/monster/wyvern.qh>
#include <common/monsters/monster/zombie.qh>
--- /dev/null
+#include "golem.qh"
+
+#ifdef SVQC
+float autocvar_g_monster_golem_health;
+float autocvar_g_monster_golem_damageforcescale = 0.1;
+float autocvar_g_monster_golem_attack_smash_damage;
+float autocvar_g_monster_golem_attack_smash_force = 100;
+float autocvar_g_monster_golem_attack_smash_range = 200;
+float autocvar_g_monster_golem_attack_claw_damage;
+float autocvar_g_monster_golem_attack_lightning_damage;
+float autocvar_g_monster_golem_attack_lightning_damage_zap = 15;
+float autocvar_g_monster_golem_attack_lightning_force;
+float autocvar_g_monster_golem_attack_lightning_radius;
+float autocvar_g_monster_golem_attack_lightning_radius_zap;
+float autocvar_g_monster_golem_attack_lightning_speed;
+float autocvar_g_monster_golem_attack_lightning_speed_up;
+float autocvar_g_monster_golem_speed_stop;
+float autocvar_g_monster_golem_speed_run;
+float autocvar_g_monster_golem_speed_walk;
+
+/*
+const float golem_anim_stand = 0;
+const float golem_anim_walk = 1;
+const float golem_anim_run = 2;
+const float golem_anim_smash = 3;
+const float golem_anim_swingr = 4;
+const float golem_anim_swingl = 5;
+const float golem_anim_magic = 6;
+const float golem_anim_pain = 7;
+const float golem_anim_death = 8;
+*/
+
+.float golem_lastattack; // delay attacks separately
+
+void M_Golem_Attack_Smash(entity this)
+{
+ makevectors(this.angles);
+ Send_Effect(EFFECT_EXPLOSION_MEDIUM, (this.origin + (v_forward * 150)) - ('0 0 1' * this.maxs.z), '0 0 0', 1);
+ sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
+
+ vector loc = this.origin + v_forward * 50;
+
+ entity dmgent = spawn();
+ dmgent.owner = dmgent.realowner = this;
+ setorigin(dmgent, loc);
+ RadiusDamage (dmgent, this, (autocvar_g_monster_golem_attack_smash_damage) * MONSTER_SKILLMOD(this), (autocvar_g_monster_golem_attack_smash_damage * 0.5) * MONSTER_SKILLMOD(this),
+ autocvar_g_monster_golem_attack_smash_range, this, NULL, autocvar_g_monster_golem_attack_smash_force, DEATH_MONSTER_GOLEM_SMASH.m_id, DMG_NOWEP, NULL);
+ delete(dmgent);
+}
+
+void M_Golem_Attack_Swing(entity this)
+{
+ Monster_Attack_Melee(this, this.enemy, (autocvar_g_monster_golem_attack_claw_damage), ((random() >= 0.5) ? this.anim_melee2 : this.anim_melee3), this.attack_range, 0.8, DEATH_MONSTER_GOLEM_CLAW.m_id, true);
+}
+
+#include <common/effects/qc/_mod.qh>
+
+void M_Golem_Attack_Lightning_Explode(entity this, entity directhitentity)
+{
+ sound(this, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
+ Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1);
+
+ this.event_damage = func_null;
+ this.takedamage = DAMAGE_NO;
+ set_movetype(this, MOVETYPE_NONE);
+ this.velocity = '0 0 0';
+
+ if(this.move_movetype == MOVETYPE_NONE)
+ this.velocity = this.oldvelocity;
+
+ RadiusDamage (this, this.realowner, (autocvar_g_monster_golem_attack_lightning_damage), (autocvar_g_monster_golem_attack_lightning_damage), (autocvar_g_monster_golem_attack_lightning_radius),
+ NULL, NULL, (autocvar_g_monster_golem_attack_lightning_force), this.projectiledeathtype, DMG_NOWEP, directhitentity);
+
+ FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_golem_attack_lightning_radius_zap, it != this.realowner && it.takedamage,
+ {
+ te_csqc_lightningarc(this.origin, it.origin);
+ Damage(it, this, this.realowner, (autocvar_g_monster_golem_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_GOLEM_ZAP.m_id, DMG_NOWEP, it.origin, '0 0 0');
+ });
+
+ setthink(this, SUB_Remove);
+ this.nextthink = time + 0.2;
+}
+
+void M_Golem_Attack_Lightning_Explode_use(entity this, entity actor, entity trigger)
+{
+ M_Golem_Attack_Lightning_Explode(this, trigger);
+}
+
+void M_Golem_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
+{
+ if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
+ return;
+
+ if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
+ return; // g_projectiles_damage says to halt
+
+ TakeResource(this, RESOURCE_HEALTH, damage);
+
+ if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
+ W_PrepareExplosionByDamage(this, attacker, adaptor_think2use);
+}
+
+void M_Golem_Attack_Lightning_Touch(entity this, entity toucher)
+{
+ PROJECTILE_TOUCH(this, toucher);
+
+ this.use(this, NULL, toucher);
+}
+
+void M_Golem_Attack_Lightning_Think(entity this)
+{
+ this.nextthink = time;
+ if (time > this.cnt)
+ {
+ M_Golem_Attack_Lightning_Explode(this, NULL);
+ return;
+ }
+}
+
+void M_Golem_Attack_Lightning(entity this)
+{
+ entity gren;
+
+ monster_makevectors(this, this.enemy);
+
+ gren = new(grenade);
+ gren.owner = gren.realowner = this;
+ gren.bot_dodge = true;
+ gren.bot_dodgerating = (autocvar_g_monster_golem_attack_lightning_damage);
+ set_movetype(gren, MOVETYPE_BOUNCE);
+ PROJECTILE_MAKETRIGGER(gren);
+ gren.projectiledeathtype = DEATH_MONSTER_GOLEM_ZAP.m_id;
+ setorigin(gren, CENTER_OR_VIEWOFS(this));
+ setsize(gren, '-8 -8 -8', '8 8 8');
+ gren.scale = 2.5;
+
+ gren.cnt = time + 5;
+ gren.nextthink = time;
+ setthink(gren, M_Golem_Attack_Lightning_Think);
+ gren.use = M_Golem_Attack_Lightning_Explode_use;
+ settouch(gren, M_Golem_Attack_Lightning_Touch);
+
+ gren.takedamage = DAMAGE_YES;
+ SetResourceAmountExplicit(gren, RESOURCE_HEALTH, 50);
+ gren.damageforcescale = 0;
+ gren.event_damage = M_Golem_Attack_Lightning_Damage;
+ gren.damagedbycontents = true;
+ IL_PUSH(g_damagedbycontents, gren);
+ gren.missile_flags = MIF_SPLASH | MIF_ARC;
+ W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_golem_attack_lightning_speed), (autocvar_g_monster_golem_attack_lightning_speed_up), 0, 0, false);
+
+ gren.angles = vectoangles (gren.velocity);
+ gren.flags = FL_PROJECTILE;
+ IL_PUSH(g_projectiles, gren);
+ IL_PUSH(g_bot_dodge, gren);
+
+ CSQCProjectile(gren, true, PROJECTILE_GOLEM_LIGHTNING, true);
+}
+
+.int state;
+
+bool M_Golem_Attack(int attack_type, entity actor, entity targ, .entity weaponentity)
+{
+ switch(attack_type)
+ {
+ case MONSTER_ATTACK_MELEE:
+ {
+ int swing_cnt = bound(1, floor(random() * 4), 3);
+ Monster_Delay(actor, swing_cnt, 0.5, M_Golem_Attack_Swing);
+ actor.anim_finished = actor.attack_finished_single[0] = time + (0.5 * swing_cnt); // set this for the delay
+ return true;
+ }
+ case MONSTER_ATTACK_RANGED:
+ {
+ float randomness = random();
+
+ if(time >= actor.golem_lastattack) // golem doesn't attack much
+ if(IS_ONGROUND(actor))
+ if(randomness <= 0.5 && vdist(actor.enemy.origin - actor.origin, <=, autocvar_g_monster_golem_attack_smash_range))
+ {
+ setanim(actor, actor.anim_melee1, false, true, true);
+ Monster_Delay(actor, 1, 1.1, M_Golem_Attack_Smash);
+ if(actor.animstate_endtime > time)
+ actor.anim_finished = actor.animstate_endtime;
+ else
+ actor.anim_finished = time + 1.2;
+ actor.attack_finished_single[0] = actor.anim_finished + 0.2;
+ actor.state = MONSTER_ATTACK_MELEE; // kinda a melee attack
+ actor.golem_lastattack = time + 3 + random() * 1.5;
+ return true;
+ }
+ else if(randomness <= 0.1 && vdist(actor.enemy.origin - actor.origin, >=, autocvar_g_monster_golem_attack_smash_range * 1.5)) // small chance, don't want this spammed
+ {
+ setanim(actor, actor.anim_shoot, true, true, false);
+ actor.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general
+ actor.attack_finished_single[0] = time + 1.1;
+ actor.anim_finished = 1.1;
+ actor.golem_lastattack = time + 3 + random() * 1.5;
+ Monster_Delay(actor, 1, 0.6, M_Golem_Attack_Lightning);
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ return false;
+}
+
+spawnfunc(monster_golem) { Monster_Spawn(this, true, MON_GOLEM.monsterid); }
+// compatibility
+spawnfunc(monster_shambler) { spawnfunc_monster_golem(this); }
+#endif // SVQC
+
+#ifdef SVQC
+METHOD(Golem, mr_think, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ return true;
+}
+
+METHOD(Golem, mr_pain, float(Golem this, entity actor, float damage_take, entity attacker, float deathtype))
+{
+ TC(Golem, this);
+ actor.pain_finished = time + 0.5;
+ setanim(actor, ((random() >= 0.5) ? actor.anim_pain2 : actor.anim_pain1), true, true, false);
+ return damage_take;
+}
+
+METHOD(Golem, mr_death, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ setanim(actor, actor.anim_die1, false, true, true);
+ return true;
+}
+#endif
+#ifdef GAMEQC
+METHOD(Golem, mr_anim, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ vector none = '0 0 0';
+ actor.anim_idle = animfixfps(actor, '0 1 1', none);
+ actor.anim_walk = animfixfps(actor, '1 1 1', none);
+ actor.anim_run = animfixfps(actor, '2 1 1', none);
+ //actor.anim_melee1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
+ actor.anim_melee2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
+ actor.anim_melee3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ //actor.anim_melee4 = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
+ actor.anim_melee1 = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
+ actor.anim_pain1 = animfixfps(actor, '7 1 2', none); // 0.5 seconds
+ actor.anim_pain2 = animfixfps(actor, '8 1 2', none); // 0.5 seconds
+ //actor.anim_pain3 = animfixfps(actor, '9 1 2', none); // 0.5 seconds
+ //actor.anim_pain4 = animfixfps(actor, '10 1 2', none); // 0.5 seconds
+ //actor.anim_pain5 = animfixfps(actor, '11 1 2', none); // 0.5 seconds
+ //actor.anim_sight = animfixfps(actor, '12 1 5', none); // analyze models and set framerate
+ actor.anim_die1 = animfixfps(actor, '13 1 0.5', none); // 2 seconds
+ //actor.anim_dead = animfixfps(actor, '14 1 0.5', none); // 2 seconds
+ actor.anim_die2 = animfixfps(actor, '15 1 0.5', none); // 2 seconds
+ // dead2 16
+ //actor.anim_dieback = animfixfps(actor, '16 1 0.5', none); // 2 seconds
+ //actor.anim_deadback = animfixfps(actor, '17 1 0.5', none); // 2 seconds
+ //actor.anim_dead2 = animfixfps(actor, '18 1 0.5', none); // 2 seconds
+ //actor.anim_dead3 = animfixfps(actor, '19 1 0.5', none); // 2 seconds
+ //actor.anim_dead4 = animfixfps(actor, '20 1 0.5', none); // 2 seconds
+ //actor.anim_dead5 = animfixfps(actor, '21 1 0.5', none); // 2 seconds
+ //actor.anim_dead6 = animfixfps(actor, '22 1 0.5', none); // 2 seconds
+ return true;
+}
+#endif
+#ifdef SVQC
+.float animstate_endtime;
+METHOD(Golem, mr_setup, bool(Golem this, entity actor))
+{
+ TC(Golem, this);
+ if(!GetResourceAmount(actor, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_golem_health);
+ if(!actor.attack_range) actor.attack_range = 150;
+ if(!actor.speed) { actor.speed = (autocvar_g_monster_golem_speed_walk); }
+ if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_golem_speed_run); }
+ if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_golem_speed_stop); }
+ if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_golem_damageforcescale); }
+
+ actor.monster_loot = ITEM_HealthMega;
+ actor.weapon = WEP_ELECTRO.m_id; // matches attacks better than WEP_VORTEX
+
+ setanim(actor, actor.anim_shoot, false, true, true);
+ actor.spawn_time = actor.animstate_endtime;
+ actor.spawnshieldtime = actor.spawn_time;
+ actor.monster_attackfunc = M_Golem_Attack;
+
+ return true;
+}
+#endif
--- /dev/null
+#pragma once
+
+#include "../all.qh"
+
+#ifdef GAMEQC
+MODEL(MON_GOLEM, M_Model("golem.dpm"));
+#endif
+
+CLASS(Golem, Monster)
+ ATTRIB(Golem, spawnflags, int, MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
+ ATTRIB(Golem, m_mins, vector, '-24 -24 -20');
+ ATTRIB(Golem, m_maxs, vector, '24 24 88');
+#ifdef GAMEQC
+ ATTRIB(Golem, m_model, Model, MDL_MON_GOLEM);
+#endif
+ ATTRIB(Golem, netname, string, "golem");
+ ATTRIB(Golem, monster_name, string, _("Golem"));
+ENDCLASS(Golem)
+
+REGISTER_MONSTER(GOLEM, NEW(Golem));
}
case 1:
{
- if(GetResourceAmount(this, RESOURCE_CELLS)) GiveResourceWithLimit(it, RESOURCE_CELLS, 1, g_pickup_cells_max);
- if(GetResourceAmount(this, RESOURCE_PLASMA)) GiveResourceWithLimit(it, RESOURCE_PLASMA, 1, g_pickup_plasma_max);
- if(GetResourceAmount(this, RESOURCE_ROCKETS)) GiveResourceWithLimit(it, RESOURCE_ROCKETS, 1, g_pickup_rockets_max);
- if(GetResourceAmount(this, RESOURCE_SHELLS)) GiveResourceWithLimit(it, RESOURCE_SHELLS, 2, g_pickup_shells_max);
- if(GetResourceAmount(this, RESOURCE_BULLETS)) GiveResourceWithLimit(it, RESOURCE_BULLETS, 5, g_pickup_nails_max);
+ if(GetResourceAmount(it, RESOURCE_CELLS)) GiveResourceWithLimit(it, RESOURCE_CELLS, 1, g_pickup_cells_max);
+ if(GetResourceAmount(it, RESOURCE_PLASMA)) GiveResourceWithLimit(it, RESOURCE_PLASMA, 1, g_pickup_plasma_max);
+ if(GetResourceAmount(it, RESOURCE_ROCKETS)) GiveResourceWithLimit(it, RESOURCE_ROCKETS, 1, g_pickup_rockets_max);
+ if(GetResourceAmount(it, RESOURCE_SHELLS)) GiveResourceWithLimit(it, RESOURCE_SHELLS, 2, g_pickup_shells_max);
+ if(GetResourceAmount(it, RESOURCE_BULLETS)) GiveResourceWithLimit(it, RESOURCE_BULLETS, 5, g_pickup_nails_max);
// TODO: fuel?
fx = EFFECT_AMMO_REGEN;
break;
{
setanim(this, this.anim_shoot, true, true, true);
this.attack_finished_single[0] = time + (autocvar_g_monster_mage_heal_delay);
+ this.state = MONSTER_ATTACK_MELEE;
this.anim_finished = time + 1.5;
}
}
{
TC(Mage, this);
vector none = '0 0 0';
- actor.anim_die1 = animfixfps(actor, '4 1 0.5', none); // 2 seconds
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
actor.anim_idle = animfixfps(actor, '0 1 1', none);
- actor.anim_pain1 = animfixfps(actor, '3 1 2', none); // 0.5 seconds
+ actor.anim_walk = animfixfps(actor, '1 1 1', none);
+ actor.anim_run = animfixfps(actor, '1 1 1', none);
actor.anim_shoot = animfixfps(actor, '2 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '5 1 1', none);
+ //actor.anim_fire1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
+ //actor.anim_fire2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
+ //actor.anim_fire3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ actor.anim_pain1 = animfixfps(actor, '6 1 2', none); // 0.5 seconds
+ actor.anim_pain2 = animfixfps(actor, '7 1 2', none); // 0.5 seconds
+ //actor.anim_pain3 = animfixfps(actor, '8 1 2', none); // 0.5 seconds
+ actor.anim_die1 = animfixfps(actor, '9 1 0.5', none); // 2 seconds
+ actor.anim_die2 = animfixfps(actor, '10 1 0.5', none); // 2 seconds
+ //actor.anim_dead1 = animfixfps(actor, '11 1 0.5', none); // 2 seconds
+ //actor.anim_dead2 = animfixfps(actor, '12 1 0.5', none); // 2 seconds
return true;
}
#endif
#include "../all.qh"
#ifdef GAMEQC
-MODEL(MON_MAGE, M_Model("mage.dpm"));
+MODEL(MON_MAGE, M_Model("nanomage.dpm"));
#endif
CLASS(Mage, Monster)
ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED);
- ATTRIB(Mage, m_mins, vector, '-36 -36 -24');
- ATTRIB(Mage, m_maxs, vector, '36 36 50');
+ ATTRIB(Mage, m_mins, vector, '-16 -16 -24');
+ ATTRIB(Mage, m_maxs, vector, '16 16 55');
#ifdef GAMEQC
ATTRIB(Mage, m_model, Model, MDL_MON_MAGE);
#endif
+++ /dev/null
-#include "shambler.qh"
-
-#ifdef SVQC
-float autocvar_g_monster_shambler_health;
-float autocvar_g_monster_shambler_damageforcescale = 0.1;
-float autocvar_g_monster_shambler_attack_smash_damage;
-float autocvar_g_monster_shambler_attack_smash_range;
-float autocvar_g_monster_shambler_attack_claw_damage;
-float autocvar_g_monster_shambler_attack_lightning_damage;
-float autocvar_g_monster_shambler_attack_lightning_damage_zap = 15;
-float autocvar_g_monster_shambler_attack_lightning_force;
-float autocvar_g_monster_shambler_attack_lightning_radius;
-float autocvar_g_monster_shambler_attack_lightning_radius_zap;
-float autocvar_g_monster_shambler_attack_lightning_speed;
-float autocvar_g_monster_shambler_attack_lightning_speed_up;
-float autocvar_g_monster_shambler_speed_stop;
-float autocvar_g_monster_shambler_speed_run;
-float autocvar_g_monster_shambler_speed_walk;
-
-/*
-const float shambler_anim_stand = 0;
-const float shambler_anim_walk = 1;
-const float shambler_anim_run = 2;
-const float shambler_anim_smash = 3;
-const float shambler_anim_swingr = 4;
-const float shambler_anim_swingl = 5;
-const float shambler_anim_magic = 6;
-const float shambler_anim_pain = 7;
-const float shambler_anim_death = 8;
-*/
-
-.float shambler_lastattack; // delay attacks separately
-
-void M_Shambler_Attack_Smash(entity this)
-{
- makevectors(this.angles);
- Send_Effect(EFFECT_EXPLOSION_MEDIUM, (this.origin + (v_forward * 150)) - ('0 0 1' * this.maxs.z), '0 0 0', 1);
- sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM);
-
- // RadiusDamage does NOT support custom starting location, which means we must use this hack...
-
- tracebox(this.origin + v_forward * 50, this.mins * 0.5, this.maxs * 0.5, this.origin + v_forward * autocvar_g_monster_shambler_attack_smash_range, MOVE_NORMAL, this);
-
- if(trace_ent.takedamage)
- Damage(trace_ent, this, this, (autocvar_g_monster_shambler_attack_smash_damage) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_SMASH.m_id, DMG_NOWEP, trace_ent.origin, normalize(trace_ent.origin - this.origin));
-}
-
-void M_Shambler_Attack_Swing(entity this)
-{
- Monster_Attack_Melee(this, this.enemy, (autocvar_g_monster_shambler_attack_claw_damage), ((random() >= 0.5) ? this.anim_melee2 : this.anim_melee3), this.attack_range, 0.8, DEATH_MONSTER_SHAMBLER_CLAW.m_id, true);
-}
-
-#include <common/effects/qc/_mod.qh>
-
-void M_Shambler_Attack_Lightning_Explode(entity this, entity directhitentity)
-{
- sound(this, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM);
- Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1);
-
- this.event_damage = func_null;
- this.takedamage = DAMAGE_NO;
- set_movetype(this, MOVETYPE_NONE);
- this.velocity = '0 0 0';
-
- if(this.move_movetype == MOVETYPE_NONE)
- this.velocity = this.oldvelocity;
-
- RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius),
- NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, DMG_NOWEP, directhitentity);
-
- FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_shambler_attack_lightning_radius_zap, it != this.realowner && it.takedamage,
- {
- te_csqc_lightningarc(this.origin, it.origin);
- Damage(it, this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_ZAP.m_id, DMG_NOWEP, it.origin, '0 0 0');
- });
-
- setthink(this, SUB_Remove);
- this.nextthink = time + 0.2;
-}
-
-void M_Shambler_Attack_Lightning_Explode_use(entity this, entity actor, entity trigger)
-{
- M_Shambler_Attack_Lightning_Explode(this, trigger);
-}
-
-void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force)
-{
- if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
- return;
-
- if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions
- return; // g_projectiles_damage says to halt
-
- TakeResource(this, RESOURCE_HEALTH, damage);
-
- if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0)
- W_PrepareExplosionByDamage(this, attacker, adaptor_think2use);
-}
-
-void M_Shambler_Attack_Lightning_Touch(entity this, entity toucher)
-{
- PROJECTILE_TOUCH(this, toucher);
-
- this.use(this, NULL, toucher);
-}
-
-void M_Shambler_Attack_Lightning_Think(entity this)
-{
- this.nextthink = time;
- if (time > this.cnt)
- {
- M_Shambler_Attack_Lightning_Explode(this, NULL);
- return;
- }
-}
-
-void M_Shambler_Attack_Lightning(entity this)
-{
- monster_makevectors(this, this.enemy);
-
- entity gren = new(grenade);
- gren.owner = gren.realowner = this;
- gren.bot_dodge = true;
- gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage);
- set_movetype(gren, MOVETYPE_BOUNCE);
- PROJECTILE_MAKETRIGGER(gren);
- gren.projectiledeathtype = DEATH_MONSTER_SHAMBLER_ZAP.m_id;
- setorigin(gren, CENTER_OR_VIEWOFS(this));
- setsize(gren, '-8 -8 -8', '8 8 8');
- gren.scale = 2.5;
-
- gren.cnt = time + 5;
- gren.nextthink = time;
- setthink(gren, M_Shambler_Attack_Lightning_Think);
- gren.use = M_Shambler_Attack_Lightning_Explode_use;
- settouch(gren, M_Shambler_Attack_Lightning_Touch);
-
- gren.takedamage = DAMAGE_YES;
- SetResourceAmountExplicit(gren, RESOURCE_HEALTH, 50);
- gren.damageforcescale = 0;
- gren.event_damage = M_Shambler_Attack_Lightning_Damage;
- gren.damagedbycontents = true;
- IL_PUSH(g_damagedbycontents, gren);
- gren.missile_flags = MIF_SPLASH | MIF_ARC;
- W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false);
-
- gren.angles = vectoangles (gren.velocity);
- gren.flags = FL_PROJECTILE;
- IL_PUSH(g_projectiles, gren);
- IL_PUSH(g_bot_dodge, gren);
-
- CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true);
-}
-
-.int state;
-
-bool M_Shambler_Attack(int attack_type, entity actor, entity targ, .entity weaponentity)
-{
- switch(attack_type)
- {
- case MONSTER_ATTACK_MELEE:
- {
- int swing_cnt = bound(1, floor(random() * 4), 3);
- Monster_Delay(actor, swing_cnt, 0.5, M_Shambler_Attack_Swing);
- actor.anim_finished = actor.attack_finished_single[0] = time + (0.5 * swing_cnt); // set this for the delay
- return true;
- }
- case MONSTER_ATTACK_RANGED:
- {
- float randomness = random();
-
- if(time >= actor.shambler_lastattack) // shambler doesn't attack much
- if(IS_ONGROUND(actor))
- if(randomness <= 0.5 && vdist(actor.enemy.origin - actor.origin, <=, autocvar_g_monster_shambler_attack_smash_range))
- {
- setanim(actor, actor.anim_melee2, true, true, false);
- Monster_Delay(actor, 1, 0.7, M_Shambler_Attack_Smash);
- actor.attack_finished_single[0] = time + 1.1;
- actor.anim_finished = time + 1.1;
- actor.state = MONSTER_ATTACK_MELEE; // kinda a melee attack
- actor.shambler_lastattack = time + 3 + random() * 1.5;
- return true;
- }
- else if(randomness <= 0.1 && vdist(actor.enemy.origin - actor.origin, >=, autocvar_g_monster_shambler_attack_smash_range * 1.5)) // small chance, don't want this spammed
- {
- setanim(actor, actor.anim_shoot, true, true, false);
- actor.state = MONSTER_ATTACK_MELEE; // maybe we should rename this to something more general
- actor.attack_finished_single[0] = time + 1.1;
- actor.anim_finished = 1.1;
- actor.shambler_lastattack = time + 3 + random() * 1.5;
- Monster_Delay(actor, 1, 0.6, M_Shambler_Attack_Lightning);
- return true;
- }
-
- return false;
- }
- }
-
- return false;
-}
-
-spawnfunc(monster_shambler) { Monster_Spawn(this, true, MON_SHAMBLER.monsterid); }
-#endif // SVQC
-
-#ifdef SVQC
-METHOD(Shambler, mr_think, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- return true;
-}
-
-METHOD(Shambler, mr_pain, float(Shambler this, entity actor, float damage_take, entity attacker, float deathtype))
-{
- TC(Shambler, this);
- actor.pain_finished = time + 0.5;
- setanim(actor, actor.anim_pain1, true, true, false);
- return damage_take;
-}
-
-METHOD(Shambler, mr_death, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- setanim(actor, actor.anim_die1, false, true, true);
- return true;
-}
-#endif
-#ifdef GAMEQC
-METHOD(Shambler, mr_anim, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- vector none = '0 0 0';
- actor.anim_die1 = animfixfps(actor, '8 1 0.5', none); // 2 seconds
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
- actor.anim_idle = animfixfps(actor, '0 1 1', none);
- actor.anim_pain1 = animfixfps(actor, '7 1 2', none); // 0.5 seconds
- actor.anim_melee1 = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
- actor.anim_melee2 = animfixfps(actor, '4 1 5', none); // analyze models and set framerate
- actor.anim_melee3 = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
- actor.anim_shoot = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '2 1 1', none);
- return true;
-}
-#endif
-#ifdef SVQC
-.float animstate_endtime;
-METHOD(Shambler, mr_setup, bool(Shambler this, entity actor))
-{
- TC(Shambler, this);
- if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_shambler_health);
- if(!actor.attack_range) actor.attack_range = 150;
- if(!actor.speed) { actor.speed = (autocvar_g_monster_shambler_speed_walk); }
- if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_shambler_speed_run); }
- if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_shambler_speed_stop); }
- if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_shambler_damageforcescale); }
-
- actor.monster_loot = ITEM_HealthMega;
- actor.weapon = WEP_ELECTRO.m_id; // matches attacks better than WEP_VORTEX
-
- setanim(actor, actor.anim_shoot, false, true, true);
- actor.spawn_time = actor.animstate_endtime;
- actor.spawnshieldtime = actor.spawn_time;
- actor.monster_attackfunc = M_Shambler_Attack;
-
- return true;
-}
-#endif
+++ /dev/null
-#pragma once
-
-#include "../all.qh"
-
-#ifdef GAMEQC
-MODEL(MON_SHAMBLER, M_Model("shambler.mdl"));
-#endif
-
-CLASS(Shambler, Monster)
- ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED);
- ATTRIB(Shambler, m_mins, vector, '-41 -41 -31');
- ATTRIB(Shambler, m_maxs, vector, '41 41 65');
-#ifdef GAMEQC
- ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER);
-#endif
- ATTRIB(Shambler, netname, string, "shambler");
- ATTRIB(Shambler, monster_name, string, _("Shambler"));
-ENDCLASS(Shambler)
-
-REGISTER_MONSTER(SHAMBLER, NEW(Shambler));
float autocvar_g_monster_spider_attack_web_speed;
float autocvar_g_monster_spider_attack_web_speed_up;
float autocvar_g_monster_spider_attack_web_delay;
+float autocvar_g_monster_spider_attack_web_range = 800;
float autocvar_g_monster_spider_attack_bite_damage;
float autocvar_g_monster_spider_attack_bite_delay;
TC(SpiderAttack, thiswep);
bool isPlayer = IS_PLAYER(actor);
if (fire & 1)
- if ((!isPlayer && time >= actor.spider_web_delay) || weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_monster_spider_attack_web_delay)) {
+ if ((!isPlayer && time >= actor.spider_web_delay) || (isPlayer && weapon_prepareattack(thiswep, actor, weaponentity, false, autocvar_g_monster_spider_attack_web_delay))) {
if (!isPlayer) {
- actor.spider_web_delay = time + 3;
+ actor.spider_web_delay = time + autocvar_g_monster_spider_attack_web_delay;
setanim(actor, actor.anim_shoot, true, true, true);
- actor.attack_finished_single[0] = time + (autocvar_g_monster_spider_attack_web_delay);
- actor.anim_finished = time + 1;
+ if(actor.animstate_endtime > time)
+ actor.anim_finished = actor.animstate_endtime;
+ else
+ actor.anim_finished = time + 1;
+ actor.attack_finished_single[0] = actor.anim_finished + 0.2;
}
if (isPlayer) actor.enemy = Monster_FindTarget(actor);
W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_SpiderAttack_FIRE, CH_WEAPON_B, 0, DEATH_MONSTER_SPIDER.m_id);
}
case MONSTER_ATTACK_RANGED:
{
- wep.wr_think(wep, actor, weaponentity, 1);
- return true;
+ if(vdist(actor.enemy.origin - actor.origin, <=, autocvar_g_monster_spider_attack_web_range))
+ {
+ wep.wr_think(wep, actor, weaponentity, 1);
+ return true;
+ }
}
}
METHOD(Spider, mr_pain, float(Spider this, entity actor, float damage_take, entity attacker, float deathtype))
{
TC(Spider, this);
+ setanim(actor, ((random() > 0.5) ? actor.anim_pain2 : actor.anim_pain1), true, true, false);
+ actor.pain_finished = actor.animstate_endtime;
return damage_take;
}
METHOD(Spider, mr_death, bool(Spider this, entity actor))
{
TC(Spider, this);
- setanim(actor, actor.anim_melee, false, true, true);
- actor.angles_x = 180;
+ setanim(actor, ((random() > 0.5) ? actor.anim_die2 : actor.anim_die1), false, true, true);
return true;
}
#endif
{
TC(Spider, this);
vector none = '0 0 0';
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
- actor.anim_idle = animfixfps(actor, '0 1 1', none);
- actor.anim_melee = animfixfps(actor, '2 1 5', none); // analyze models and set framerate
- actor.anim_shoot = animfixfps(actor, '3 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '1 1 1', none);
+ actor.anim_melee = animfixfps(actor, '0 1 5', none); // analyze models and set framerate
+ actor.anim_die1 = animfixfps(actor, '1 1 1', none);
+ actor.anim_die2 = animfixfps(actor, '2 1 1', none);
+ actor.anim_shoot = animfixfps(actor, '3 1 1', none);
+ //actor.anim_fire2 = animfixfps(actor, '4 1 1', none);
+ actor.anim_idle = animfixfps(actor, '5 1 1', none);
+ //actor.anim_sight = animfixfps(actor, '6 1 1', none);
+ actor.anim_pain1 = animfixfps(actor, '7 1 1', none);
+ actor.anim_pain2 = animfixfps(actor, '8 1 1', none);
+ //actor.anim_pain3 = animfixfps(actor, '9 1 1', none);
+ actor.anim_walk = animfixfps(actor, '10 1 1', none);
+ actor.anim_run = animfixfps(actor, '10 1 1', none); // temp?
+ //actor.anim_forwardright = animfixfps(actor, '11 1 1', none);
+ //actor.anim_walkright = animfixfps(actor, '12 1 1', none);
+ //actor.anim_walkbackright = animfixfps(actor, '13 1 1', none);
+ //actor.anim_walkback = animfixfps(actor, '14 1 1', none);
+ //actor.anim_walkbackleft = animfixfps(actor, '15 1 1', none);
+ //actor.anim_walkleft = animfixfps(actor, '16 1 1', none);
+ //actor.anim_forwardleft = animfixfps(actor, '17 1 1', none);
return true;
}
#endif
CLASS(Spider, Monster)
ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE);
- ATTRIB(Spider, m_mins, vector, '-18 -18 -25');
- ATTRIB(Spider, m_maxs, vector, '18 18 30');
+ ATTRIB(Spider, m_mins, vector, '-30 -30 -25');
+ ATTRIB(Spider, m_maxs, vector, '30 30 30');
#ifdef GAMEQC
ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER);
#endif
if (fire & 1)
if (time > actor.attack_finished_single[0] || weapon_prepareattack(thiswep, actor, weaponentity, false, 1.2)) {
if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_WyvernAttack_FIRE, CH_WEAPON_B, 0, DEATH_MONSTER_WYVERN.m_id);
- if (IS_MONSTER(actor)) {
- actor.attack_finished_single[0] = time + 1.2;
- actor.anim_finished = time + 1.2;
- monster_makevectors(actor, actor.enemy);
- }
+ if (IS_MONSTER(actor)) monster_makevectors(actor, actor.enemy);
entity missile = spawn();
missile.owner = missile.realowner = actor;
entity own = this.realowner;
RadiusDamage(this, own, autocvar_g_monster_wyvern_attack_fireball_damage, autocvar_g_monster_wyvern_attack_fireball_edgedamage, autocvar_g_monster_wyvern_attack_fireball_force,
- NULL, NULL, autocvar_g_monster_wyvern_attack_fireball_radius, this.projectiledeathtype, DMG_NOWEP, NULL);
+ own, NULL, autocvar_g_monster_wyvern_attack_fireball_radius, this.projectiledeathtype, DMG_NOWEP, NULL);
- FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM,
+ FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM && it != own,
{
Fire_AddDamage(it, own, 5 * MONSTER_SKILLMOD(own), autocvar_g_monster_wyvern_attack_fireball_damagetime, this.projectiledeathtype);
});
M_Wyvern_Attack_Fireball_Explode(this);
}
+void M_Wyvern_Attack_Fireball(entity this)
+{
+ w_shotdir = normalize((this.enemy.origin + '0 0 10') - this.origin);
+ Weapon wep = WEP_WYVERN_ATTACK;
+ // TODO
+ .entity weaponentity = weaponentities[0];
+ wep.wr_think(wep, this, weaponentity, 1);
+}
+
bool M_Wyvern_Attack(int attack_type, entity actor, entity targ, .entity weaponentity)
{
switch(attack_type)
case MONSTER_ATTACK_MELEE:
case MONSTER_ATTACK_RANGED:
{
- w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin);
- Weapon wep = WEP_WYVERN_ATTACK;
- wep.wr_think(wep, actor, weaponentity, 1);
+ Monster_Delay(actor, 0, 1, M_Wyvern_Attack_Fireball);
+ //actor.anim_finished = time + 1.2;
+ setanim(actor, actor.anim_shoot, false, true, true);
+ if(actor.animstate_endtime > time)
+ actor.anim_finished = actor.animstate_endtime;
+ else
+ actor.anim_finished = time + 1.2;
+ actor.attack_finished_single[0] = actor.anim_finished + 0.2;
return true;
}
}
return true;
}
+METHOD(Wyvern, mr_deadthink, bool(Wyvern this, entity actor))
+{
+ TC(Wyvern, this);
+ if(IS_ONGROUND(actor))
+ setanim(actor, actor.anim_die2, true, false, false);
+ return true;
+}
+
METHOD(Wyvern, mr_pain, float(Wyvern this, entity actor, float damage_take, entity attacker, float deathtype))
{
TC(Wyvern, this);
{
TC(Wyvern, this);
vector none = '0 0 0';
- actor.anim_die1 = animfixfps(actor, '4 1 0.5', none); // 2 seconds
- actor.anim_walk = animfixfps(actor, '1 1 1', none);
actor.anim_idle = animfixfps(actor, '0 1 1', none);
+ actor.anim_walk = animfixfps(actor, '1 1 1', none);
+ actor.anim_run = animfixfps(actor, '2 1 1', none);
actor.anim_pain1 = animfixfps(actor, '3 1 2', none); // 0.5 seconds
- actor.anim_shoot = animfixfps(actor, '2 1 5', none); // analyze models and set framerate
- actor.anim_run = animfixfps(actor, '1 1 1', none);
+ actor.anim_pain2 = animfixfps(actor, '4 1 2', none); // 0.5 seconds
+ actor.anim_melee = animfixfps(actor, '5 1 5', none); // analyze models and set framerate
+ actor.anim_shoot = animfixfps(actor, '6 1 5', none); // analyze models and set framerate
+ actor.anim_die1 = animfixfps(actor, '7 1 0.5', none); // 2 seconds
+ actor.anim_die2 = animfixfps(actor, '8 1 0.5', none); // 2 seconds
return true;
}
#endif
#include "../all.qh"
#ifdef GAMEQC
-MODEL(MON_WYVERN, M_Model("wizard.mdl"));
+MODEL(MON_WYVERN, M_Model("dragon.dpm"));
#endif
CLASS(Wyvern, Monster)
- ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE);
- ATTRIB(Wyvern, m_mins, vector, '-20 -20 -58');
- ATTRIB(Wyvern, m_maxs, vector, '20 20 20');
+ ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MON_FLAG_RANGED | MON_FLAG_RIDE);
+ ATTRIB(Wyvern, m_mins, vector, '-30 -30 -48');
+ ATTRIB(Wyvern, m_maxs, vector, '30 30 30');
#ifdef GAMEQC
ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN);
#endif
{
TC(Zombie, this);
actor.pain_finished = time + 0.34;
- setanim(actor, ((random() > 0.5) ? actor.anim_pain1 : actor.anim_pain2), true, true, false);
+ if(time >= actor.spawn_time)
+ setanim(actor, ((random() > 0.5) ? actor.anim_pain1 : actor.anim_pain2), true, true, false);
return damage_take;
}
}
}
+bool monster_facing(entity this, entity targ)
+{
+ // relies on target having an origin
+ makevectors(this.angles);
+ vector targ_org = targ.origin, my_org = this.origin;
+ if(autocvar_g_monsters_target_infront_2d)
+ {
+ targ_org = vec2(targ_org);
+ my_org = vec2(my_org);
+ }
+ float dot = normalize(targ_org - my_org) * v_forward;
+
+ return !(dot <= autocvar_g_monsters_target_infront_range);
+}
+
void monster_makevectors(entity this, entity targ)
{
if(IS_MONSTER(this))
// Target handling
// ===============
-bool Monster_ValidTarget(entity this, entity targ)
+bool Monster_ValidTarget(entity this, entity targ, bool skipfacing)
{
// ensure we're not checking nonexistent monster/target
if(!this || !targ) { return false; }
if((targ == this)
- || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
- || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
|| (time < game_starttime) // monsters do nothing before match has started
|| (targ.takedamage == DAMAGE_NO)
|| (game_stopped)
|| (this.monster_follow == targ || targ.monster_follow == this)
|| (!IS_VEHICLE(targ) && (targ.flags & FL_NOTARGET))
|| (!autocvar_g_monsters_typefrag && PHYS_INPUT_BUTTON_CHAT(targ))
+ || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless
|| (SAME_TEAM(targ, this))
|| (STAT(FROZEN, targ))
|| (targ.alpha != 0 && targ.alpha < 0.5)
+ || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen
|| (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ))
)
{
}
vector targ_origin = ((targ.absmin + targ.absmax) * 0.5);
- traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this);
+ traceline(this.origin + this.view_ofs, targ_origin, MOVE_NOMONSTERS, this); // TODO: maybe we can rely a bit on PVS data instead?
if(trace_fraction < 1 && trace_ent != targ)
return false; // solid
- if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT))
+ if(!skipfacing && (autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)))
if(this.enemy != targ)
{
- makevectors (this.angles);
- float dot = normalize (targ.origin - this.origin) * v_forward;
-
- if(dot <= autocvar_g_monsters_target_infront_range) { return false; }
+ if(!monster_facing(this, targ))
+ return false;
}
return true; // this target is valid!
vector my_center = CENTER_OR_VIEWOFS(this);
// find the closest acceptable target to pass to
- IL_EACH(g_monster_targets, it.monster_attack && vdist(it.origin - this.origin, <, this.target_range),
+ IL_EACH(g_monster_targets, it.monster_attack,
{
- if(Monster_ValidTarget(this, it))
- {
- // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
- vector targ_center = CENTER_OR_VIEWOFS(it);
+ float trange = this.target_range;
+ if(PHYS_INPUT_BUTTON_CROUCH(it))
+ trange *= 0.75; // TODO cvar this
+ vector theirmid = (it.absmin + it.absmax) * 0.5;
+ if(vdist(theirmid - this.origin, >, trange))
+ continue;
+ if(!Monster_ValidTarget(this, it, false))
+ continue;
- if(closest_target)
- {
- vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
- if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
- { closest_target = it; }
- }
- else { closest_target = it; }
+ // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
+ vector targ_center = CENTER_OR_VIEWOFS(it);
+
+ if(closest_target)
+ {
+ vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
+ if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center))
+ { closest_target = it; }
}
+ else { closest_target = it; }
});
return closest_target;
else
this.colormap = 1024;
}
+
+ if(this.colormap > 0)
+ this.glowmod = colormapPaletteColor(this.colormap & 0x0F, false);
+ else
+ this.glowmod = '1 1 1';
}
void monster_changeteam(entity this, int newteam)
.void(entity) monster_delayedfunc;
void Monster_Delay_Action(entity this)
{
- if(Monster_ValidTarget(this.owner, this.owner.enemy)) { this.monster_delayedfunc(this.owner); }
+ // TODO: maybe do check for facing here
+ if(Monster_ValidTarget(this.owner, this.owner.enemy, false)) { this.monster_delayedfunc(this.owner); }
if(this.cnt > 1)
{
if((!this || !targ)
|| (!this.monster_attackfunc)
|| (time < this.attack_finished_single[slot])
+ || ((autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)) && !monster_facing(this, targ))
) { return; }
if(vdist(targ.origin - this.origin, <=, this.attack_range))
{
if(toucher == NULL) { return; }
- if(toucher.monster_attack)
- if(this.enemy != toucher)
- if(!IS_MONSTER(toucher))
- if(Monster_ValidTarget(this, toucher))
+ if(toucher.monster_attack && this.enemy != toucher && !IS_MONSTER(toucher) && time >= this.spawn_time)
+ if(Monster_ValidTarget(this, toucher, true))
this.enemy = toucher;
}
void Monster_Use(entity this, entity actor, entity trigger)
{
- if(Monster_ValidTarget(this, actor)) { this.enemy = actor; }
+ if(Monster_ValidTarget(this, actor, true)) { this.enemy = actor; }
}
.float pass_distance;
WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this);
// cases where the enemy may have changed their state (don't need to check everything here)
- if((!this.enemy)
- || (IS_DEAD(this.enemy) || GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 1)
+ if( (IS_DEAD(this.enemy) || GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 1)
|| (STAT(FROZEN, this.enemy))
|| (this.enemy.flags & FL_NOTARGET)
|| (this.enemy.alpha < 0.5 && this.enemy.alpha != 0)
|| (this.enemy.takedamage == DAMAGE_NO)
|| (vdist(this.origin - targ_origin, >, this.target_range))
- || ((trace_fraction < 1) && (trace_ent != this.enemy)))
+ || ((trace_fraction < 1) && (trace_ent != this.enemy))
+ )
{
this.enemy = NULL;
//this.pass_distance = 0;
}
}
-void Monster_CalculateVelocity(entity this, vector to, vector from, float turnrate, float movespeed)
-{
- //float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis
- //float initial_height = 0; //min(50, (targ_distance * tanh(20)));
- //float current_height = (initial_height * min(1, (this.pass_distance) ? (current_distance / this.pass_distance) : current_distance));
- //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
-
- vector targpos = to;
-#if 0
- if(current_height) // make sure we can actually do this arcing path
- {
- targpos = (to + ('0 0 1' * current_height));
- WarpZone_TraceLine(this.origin, targpos, MOVE_NOMONSTERS, this);
- if(trace_fraction < 1)
- {
- //print("normal arc line failed, trying to find new pos...");
- WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, this);
- targpos = (trace_endpos + '0 0 -10');
- WarpZone_TraceLine(this.origin, targpos, MOVE_NOMONSTERS, this);
- if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ }
- /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */
- }
- }
- else { targpos = to; }
-#endif
-
- //this.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y));
-
- vector desired_direction = normalize(targpos - from);
- if(turnrate) { this.velocity = (normalize(normalize(this.velocity) + (desired_direction * 50)) * movespeed); }
- else { this.velocity = (desired_direction * movespeed); }
-
- //this.steerto = steerlib_attract2(targpos, 0.5, 500, 0.95);
- //this.angles = vectoangles(this.velocity);
-}
-
.entity draggedby;
.entity target2;
if(!(this.spawnflags & MONSTERFLAG_FLY_VERTICAL) && !(this.flags & FL_SWIM))
this.moveto_z = this.origin_z;
- if(vdist(this.origin - this.moveto, >, 100))
+ fixedmakevectors(this.angles);
+ float vz = this.velocity_z;
+
+ if(!turret_closetotarget(this, this.moveto, 16))
{
bool do_run = (this.enemy || this.monster_moveto);
- if(IS_ONGROUND(this) || ((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
- Monster_CalculateVelocity(this, this.moveto, this.origin, true, ((do_run) ? runspeed : walkspeed));
+ movelib_move_simple(this, v_forward, ((do_run) ? runspeed : walkspeed), 0.4);
- if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!?
+ if(time > this.pain_finished && time > this.anim_finished)
if(!this.state)
if(vdist(this.velocity, >, 10))
setanim(this, ((do_run) ? this.anim_run : this.anim_walk), true, false, false);
setanim(this, this.anim_idle, true, false, false);
}
+ if(!((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
+ this.velocity_z = vz;
+
this.steerto = steerlib_attract2(this, ((this.monster_face) ? this.monster_face : this.moveto), 0.5, 500, 0.95);
vector real_angle = vectoangles(this.steerto) - this.angles;
{
this.nextthink = time + this.ticrate;
+ Monster mon = Monsters_from(this.monsterid);
+ mon.mr_deadthink(mon, this);
+
if(this.monster_lifetime != 0)
if(time >= this.monster_lifetime)
{
this.state = 0;
this.attack_finished_single[0] = 0;
this.effects = 0;
+ this.dphitcontentsmask &= ~DPCONTENTS_BODY;
if(!((this.flags & FL_FLY) || (this.flags & FL_SWIM)))
this.velocity = '0 0 0';
void Monster_Enemy_Check(entity this)
{
- if(!this.enemy)
+ if(this.enemy)
+ return;
+
+ this.enemy = Monster_FindTarget(this);
+ if(this.enemy)
{
- this.enemy = Monster_FindTarget(this);
- if(this.enemy)
- {
- WarpZone_RefSys_Copy(this.enemy, this);
- WarpZone_RefSys_AddInverse(this.enemy, this); // wz1^-1 ... wzn^-1 receiver
- // update move target immediately?
- this.moveto = WarpZone_RefSys_TransformOrigin(this.enemy, this, (0.5 * (this.enemy.absmin + this.enemy.absmax)));
- this.monster_moveto = '0 0 0';
- this.monster_face = '0 0 0';
-
- //this.pass_distance = vlen((('1 0 0' * this.enemy.origin_x) + ('0 1 0' * this.enemy.origin_y)) - (('1 0 0' * this.origin_x) + ('0 1 0' * this.origin_y)));
- Monster_Sound(this, monstersound_sight, 0, false, CH_VOICE);
- }
+ WarpZone_RefSys_Copy(this.enemy, this);
+ WarpZone_RefSys_AddInverse(this.enemy, this); // wz1^-1 ... wzn^-1 receiver
+ // update move target immediately?
+ this.moveto = WarpZone_RefSys_TransformOrigin(this.enemy, this, (0.5 * (this.enemy.absmin + this.enemy.absmax)));
+ this.monster_moveto = '0 0 0';
+ this.monster_face = '0 0 0';
+
+ //this.pass_distance = vlen((('1 0 0' * this.enemy.origin_x) + ('0 1 0' * this.enemy.origin_y)) - (('1 0 0' * this.origin_x) + ('0 1 0' * this.origin_y)));
+ Monster_Sound(this, monstersound_sight, 0, false, CH_VOICE);
}
}
this.max_health = GetResourceAmount(this, RESOURCE_HEALTH);
this.pain_finished = this.nextthink;
+ this.last_enemycheck = this.spawn_time + random(); // slight delay
if(IS_PLAYER(this.monster_follow))
this.effects |= EF_DIMLIGHT;
this.monster_attackfunc = mon.monster_attackfunc;
this.monster_name = mon.monster_name;
this.candrop = true;
- this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.oldtarget2 = this.target2;
//this.pass_distance = 0;
this.deadflag = DEAD_NO;
if(autocvar_g_nodepthtestplayers) { this.effects |= EF_NODEPTHTEST; }
if(mon.spawnflags & MONSTER_TYPE_SWIM) { this.flags |= FL_SWIM; }
- if(autocvar_g_playerclip_collisions)
+ if(autocvar_g_monsters_playerclip_collisions)
this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP;
if(mon.spawnflags & MONSTER_TYPE_FLY)
set_movetype(this, MOVETYPE_FLY);
}
- if(!(this.spawnflags & MONSTERFLAG_RESPAWNED))
- {
- if(mon.spawnflags & MONSTER_SIZE_BROKEN)
- this.scale *= 1.3;
-
- if(mon.spawnflags & MONSTER_SIZE_QUAKE)
- if(autocvar_g_monsters_quake_resize)
- this.scale *= 1.3;
- }
+ if((mon.spawnflags & MONSTER_SIZE_QUAKE) && autocvar_g_monsters_quake_resize && !(this.spawnflags & MONSTERFLAG_RESPAWNED))
+ this.scale *= 1.3;
setsize(this, mon.m_mins * this.scale, mon.m_maxs * this.scale);
+ this.view_ofs = '0 0 0.7' * (this.maxs_z * 0.5);
this.ticrate = bound(sys_frametime, ((!this.ticrate) ? autocvar_g_monsters_think_delay : this.ticrate), 60);
MSG_INFO_NOTIF(DEATH_SELF_GENERIC, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_selfkill", _("^BG%s^K1 died%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_LAVA, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_lava", _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s"))
MSG_INFO_NOTIF(DEATH_SELF_MON_MAGE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was exploded by a Mage%s%s"), "")
- MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_CLAW, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "")
- MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_SMASH, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was smashed by a Shambler%s%s"), "")
- MSG_INFO_NOTIF(DEATH_SELF_MON_SHAMBLER_ZAP, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was zapped to death by a Shambler%s%s"), "")
+ MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_CLAW, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1's innards became outwards by a Golem%s%s"), "")
+ MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_SMASH, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was smashed by a Golem%s%s"), "")
+ MSG_INFO_NOTIF(DEATH_SELF_MON_GOLEM_ZAP, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was zapped to death by a Golem%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_MON_SPIDER, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was bitten by a Spider%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_MON_WYVERN, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "")
MSG_INFO_NOTIF(DEATH_SELF_MON_ZOMBIE_JUMP, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 joins the Zombies%s%s"), "")
MSG_MULTI_NOTIF(DEATH_SELF_GENERIC, N_ENABLE, NULL, INFO_DEATH_SELF_GENERIC, CENTER_DEATH_SELF_GENERIC)
MSG_MULTI_NOTIF(DEATH_SELF_LAVA, N_ENABLE, NULL, INFO_DEATH_SELF_LAVA, CENTER_DEATH_SELF_LAVA)
MSG_MULTI_NOTIF(DEATH_SELF_MON_MAGE, N_ENABLE, NULL, INFO_DEATH_SELF_MON_MAGE, CENTER_DEATH_SELF_MONSTER)
- MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_CLAW, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SHAMBLER_CLAW, CENTER_DEATH_SELF_MONSTER)
- MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_SMASH, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SHAMBLER_SMASH, CENTER_DEATH_SELF_MONSTER)
- MSG_MULTI_NOTIF(DEATH_SELF_MON_SHAMBLER_ZAP, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SHAMBLER_ZAP, CENTER_DEATH_SELF_MONSTER)
+ MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_CLAW, N_ENABLE, NULL, INFO_DEATH_SELF_MON_GOLEM_CLAW, CENTER_DEATH_SELF_MONSTER)
+ MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_SMASH, N_ENABLE, NULL, INFO_DEATH_SELF_MON_GOLEM_SMASH, CENTER_DEATH_SELF_MONSTER)
+ MSG_MULTI_NOTIF(DEATH_SELF_MON_GOLEM_ZAP, N_ENABLE, NULL, INFO_DEATH_SELF_MON_GOLEM_ZAP, CENTER_DEATH_SELF_MONSTER)
MSG_MULTI_NOTIF(DEATH_SELF_MON_SPIDER, N_ENABLE, NULL, INFO_DEATH_SELF_MON_SPIDER, CENTER_DEATH_SELF_MONSTER)
MSG_MULTI_NOTIF(DEATH_SELF_MON_WYVERN, N_ENABLE, NULL, INFO_DEATH_SELF_MON_WYVERN, CENTER_DEATH_SELF_MONSTER)
MSG_MULTI_NOTIF(DEATH_SELF_MON_ZOMBIE_JUMP, N_ENABLE, NULL, INFO_DEATH_SELF_MON_ZOMBIE_JUMP, CENTER_DEATH_SELF_MONSTER)
#undef TRY
}
-bool turret_closetotarget(entity this, vector targ)
+bool turret_closetotarget(entity this, vector targ, float range)
{
- vector path_extra_size = '64 64 64';
+ vector path_extra_size = '1 1 1' * range;
return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.absmin - path_extra_size, this.absmax + path_extra_size);
}
bool turret_initialize(entity this, Turret tur);
// returns true when box overlaps with a given location
-bool turret_closetotarget(entity this, vector targ);
+bool turret_closetotarget(entity this, vector targ, float range);
/// Function to use for target evaluation. usualy turret_targetscore_generic
.float(entity _turret, entity _target) turret_score_target;
void ewheel_move_path(entity this)
{
// Are we close enough to a path node to switch to the next?
- if(turret_closetotarget(this, this.pathcurrent.origin))
+ if(turret_closetotarget(this, this.pathcurrent.origin, 64))
{
#ifdef EWHEEL_FANCYPATH
if (this.pathcurrent.path_next == NULL)
{
#ifdef WALKER_FANCYPATHING
// Are we close enougth to a path node to switch to the next?
- if(turret_closetotarget(this, this.pathcurrent.origin))
+ if(turret_closetotarget(this, this.pathcurrent.origin, 64))
{
if (this.pathcurrent.path_next == NULL)
{
walker_move_to(this, this.moveto, 0);
#else
- if(turret_closetotarget(this, this.pathcurrent.origin))
+ if(turret_closetotarget(this, this.pathcurrent.origin, 64))
this.pathcurrent = this.pathcurrent.enemy;
if(!this.pathcurrent)
const int PROJECTILE_BUMBLE_BEAM = 31;
const int PROJECTILE_MAGE_SPIKE = 32;
-const int PROJECTILE_SHAMBLER_LIGHTNING = 33;
+const int PROJECTILE_GOLEM_LIGHTNING = 33;
const int PROJECTILE_ROCKETMINSTA_LASER = 34;
me.TR(me);
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "zombie", _("Zombie")));
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "spider", _("Spider")));
- me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "shambler", _("Shambler")));
+ me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "golem", _("Golem")));
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "mage", _("Mage")));
me.TD(me, 1, 0.4, e = makeXonoticRadioButton(2, "menu_monsters_edit_spawn", "wyvern", _("Wyvern")));
me.TR(me);
GAMETYPE(MAPINFO_TYPE_NEXBALL) \
GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \
GAMETYPE(MAPINFO_TYPE_ASSAULT) \
- /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \
/**/
// hidden gametypes come last so indexing always works correctly
#define HIDDEN_GAMETYPES \
GAMETYPE(MAPINFO_TYPE_RACE) \
GAMETYPE(MAPINFO_TYPE_CTS) \
+ GAMETYPE(MAPINFO_TYPE_INVASION) \
/**/
Gametype GameType_GetID(int cnt)
float autocvar_g_monsters_target_range;
bool autocvar_g_monsters_target_infront;
float autocvar_g_monsters_target_infront_range = 0.3;
+bool autocvar_g_monsters_target_infront_2d = true;
float autocvar_g_monsters_attack_range;
int autocvar_g_monsters_score_kill;
int autocvar_g_monsters_score_spawned;
bool autocvar_g_monsters_typefrag;
bool autocvar_g_monsters_owners;
+bool autocvar_g_monsters_playerclip_collisions;
float autocvar_g_monsters_miniboss_chance;
float autocvar_g_monsters_miniboss_healthboost;
float autocvar_g_monsters_drop_time;
--- /dev/null
+golem
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/golem.tga
+ rgbgen lightingDiffuse
+ }
+}
\ No newline at end of file
--- /dev/null
+nanomage
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/nanomage.tga
+ rgbgen lightingDiffuse
+ }
+}
\ No newline at end of file
--- /dev/null
+spider
+{
+ dpreflectcube cubemaps/default/sky
+ {
+ map textures/spider.tga
+ rgbgen lightingDiffuse
+ }
+}
\ No newline at end of file