]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Fix invasion monster spawn counts, fix spawning random monsters, clean up monster...
authorMario <mario.mario@y7mail.com>
Tue, 18 Mar 2014 05:13:47 +0000 (16:13 +1100)
committerMario <mario.mario@y7mail.com>
Tue, 18 Mar 2014 05:13:47 +0000 (16:13 +1100)
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/monster/wyvern.qc
qcsrc/common/monsters/monster/zombie.qc
qcsrc/common/monsters/spawn.qc
qcsrc/common/monsters/spawn.qh
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/monsters/sv_monsters.qh
qcsrc/server/command/cmd.qc
qcsrc/server/mutators/gamemode_invasion.qc

index 2c8ebd5479d4676d8dc85f2fb54b777826c8d549..889a88123e69ea630e12a38ab417e755c89fc664 100644 (file)
@@ -233,7 +233,8 @@ void mage_heal()
                {
                        pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1);
                        head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health);
-                       WaypointSprite_UpdateHealth(head.sprite, head.health);
+                       if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE))
+                               WaypointSprite_UpdateHealth(head.sprite, head.health);
                }
        }
 
@@ -335,12 +336,10 @@ void spawnfunc_monster_mage()
 {
        self.classname = "monster_mage";
 
-       self.monster_spawnfunc = spawnfunc_monster_mage;
-
-       if(Monster_CheckAppearFlags(self))
+       if(Monster_CheckAppearFlags(self, MON_MAGE))
                return;
 
-       if(!monster_initialize(MON_MAGE, FALSE)) { remove(self); return; }
+       if(!monster_initialize(MON_MAGE)) { remove(self); return; }
 }
 
 // compatibility with old spawns
index 866782d979771dac43d276690be865ec0a9ad1a1..c95fbebdbe078bee4434f28ff6ac48a38294a0e1 100644 (file)
@@ -168,6 +168,7 @@ float shambler_attack(float attack_type)
                case MONSTER_ATTACK_RANGED:
                {
                        if(time >= self.shambler_lastattack) // shambler doesn't attack much
+                       if(self.flags & FL_ONGROUND)
                        if(random() <= 0.5 && vlen(self.enemy.origin - self.origin) <= 500)
                        {
                                self.frame = shambler_anim_smash;
@@ -196,12 +197,10 @@ void spawnfunc_monster_shambler()
 {
        self.classname = "monster_shambler";
 
-       self.monster_spawnfunc = spawnfunc_monster_shambler;
-
-       if(Monster_CheckAppearFlags(self))
+       if(Monster_CheckAppearFlags(self, MON_SHAMBLER))
                return;
 
-       if(!monster_initialize(MON_SHAMBLER, FALSE)) { remove(self); return; }
+       if(!monster_initialize(MON_SHAMBLER)) { remove(self); return; }
 }
 
 float m_shambler(float req)
index 0f46a9620e6683c5d6b8e6a1bcebd94a268d9f33..9acffb5f48ab2196f998c5cb8730ee28290b7dc4 100644 (file)
@@ -119,12 +119,10 @@ void spawnfunc_monster_spider()
 {
        self.classname = "monster_spider";
 
-       self.monster_spawnfunc = spawnfunc_monster_spider;
-
-       if(Monster_CheckAppearFlags(self))
+       if(Monster_CheckAppearFlags(self, MON_SPIDER))
                return;
 
-       if(!monster_initialize(MON_SPIDER, FALSE)) { remove(self); return; }
+       if(!monster_initialize(MON_SPIDER)) { remove(self); return; }
 }
 
 float m_spider(float req)
index 2d72b4b0526e5dd84727262372a38708f9bbc011..26dbb186c07a0dd00a826ac76779bce73c27c039 100644 (file)
@@ -96,12 +96,10 @@ void spawnfunc_monster_wyvern()
 {
        self.classname = "monster_wyvern";
 
-       self.monster_spawnfunc = spawnfunc_monster_wyvern;
-
-       if(Monster_CheckAppearFlags(self))
+       if(Monster_CheckAppearFlags(self, MON_WYVERN))
                return;
 
-       if(!monster_initialize(MON_WYVERN, TRUE)) { remove(self); return; }
+       if(!monster_initialize(MON_WYVERN)) { remove(self); return; }
 }
 
 // compatibility with old spawns
index e5155b8aedac5e2a98194838244424eb024a5dcf..4efb7cc1b7726738afeafe2a0181ffcd1680665f 100644 (file)
@@ -130,14 +130,10 @@ void spawnfunc_monster_zombie()
 {
        self.classname = "monster_zombie";
 
-       self.monster_spawnfunc = spawnfunc_monster_zombie;
-
-       self.spawnflags |= MONSTER_RESPAWN_DEATHPOINT;
-
-       if(Monster_CheckAppearFlags(self))
+       if(Monster_CheckAppearFlags(self, MON_ZOMBIE))
                return;
 
-       if(!monster_initialize(MON_ZOMBIE, FALSE)) { remove(self); return; }
+       if(!monster_initialize(MON_ZOMBIE)) { remove(self); return; }
 }
 
 float m_zombie(float req)
@@ -163,6 +159,8 @@ float m_zombie(float req)
                        if(self.spawnflags & MONSTERFLAG_NORESPAWN)
                                self.spawnflags &= ~MONSTERFLAG_NORESPAWN; // zombies always respawn
 
+                       self.spawnflags |= MONSTER_RESPAWN_DEATHPOINT;
+
                        self.monster_loot = spawnfunc_item_health_medium;
                        self.monster_attackfunc = zombie_attack;
                        self.frame = zombie_anim_spawn;
index 924a728a07678ed0101b537a35ba07a8997f8d69..be5accf5ede2d095a2d40cc398970aa406a49a74 100644 (file)
@@ -1,7 +1,7 @@
-entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float moveflag)
+entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag)
 {
        // ensure spawnfunc database is initialized
-       initialize_field_db();
+       //initialize_field_db();
 
        entity e = spawn();
        float i;
@@ -11,6 +11,9 @@ entity spawnmonster (string monster, float monster_id, entity spawnedby, entity
        if(!respwn)
                e.spawnflags |= MONSTERFLAG_NORESPAWN;
 
+       if(invincible)
+               e.spawnflags |= MONSTERFLAG_INVINCIBLE;
+
        setorigin(e, orig);
 
        if(monster == "random")
@@ -32,6 +35,7 @@ entity spawnmonster (string monster, float monster_id, entity spawnedby, entity
                        if(mon.netname == monster)
                        {
                                found = TRUE;
+                               monster_id = mon.monsterid; // we have the monster, old monster id is no longer required
                                break;
                        }
                }
@@ -59,9 +63,14 @@ entity spawnmonster (string monster, float monster_id, entity spawnedby, entity
                e.angles = spawnedby.angles;
        }
 
-       monster = strcat("$ spawnfunc_monster_", monster);
+       //monster = strcat("$ spawnfunc_monster_", monster);
+       
+       entity oldself = self;
+       self = e;
+       monster_initialize(monster_id);
+       self = oldself;
 
-       target_spawn_edit_entity(e, monster, world, world, world, world, world);
+       //target_spawn_edit_entity(e, monster, world, world, world, world, world);
 
        return e;
 }
index d3d3fcb34dd79414079bac6646813ed036b27c38..02d308677c708f3d6e0fcc2211463e73be4cc7e0 100644 (file)
@@ -1 +1 @@
-entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float moveflag);
+entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag);
index 927501e654b81e11171198ed9f4102a0f9ecde13..15fa1608e5836762c60e0d32705a5257adecdd1e 100644 (file)
@@ -3,30 +3,13 @@
 // =========================
 
 
-void monster_item_spawn()
-{
-       if(self.monster_loot)
-               self.monster_loot();
-
-       self.gravity = 1;
-       self.reset = SUB_Remove;
-       self.noalign = TRUE;
-       self.velocity = randomvec() * 175 + '0 0 325';
-       self.classname = "droppedweapon"; // hax
-       self.item_spawnshieldtime = time + 0.7;
-
-       SUB_SetFade(self, time + autocvar_g_monsters_drop_time, 1);
-}
-
 void monster_dropitem()
 {
        if(!self.candrop || !self.monster_loot)
                return;
 
        vector org = self.origin + ((self.mins + self.maxs) * 0.5);
-       entity e = spawn();
-
-       setorigin(e, org);
+       entity e = spawn(), oldself = self;
 
        e.monster_loot = self.monster_loot;
 
@@ -34,10 +17,20 @@ void monster_dropitem()
        MUTATOR_CALLHOOK(MonsterDropItem);
        e = other;
 
-       if(e)
+       if(e && e.monster_loot)
        {
-               e.think = monster_item_spawn;
-               e.nextthink = time + 0.3;
+               self = e;
+               e.noalign = TRUE;
+               e.monster_loot();
+               e.gravity = 1;
+               e.movetype = MOVETYPE_TOSS;
+               e.reset = SUB_Remove;
+               setorigin(e, org);
+               e.velocity = randomvec() * 175 + '0 0 325';
+               e.item_spawnshieldtime = time + 0.7;
+               e.classname = "droppedweapon"; // use weapon handling to remove it on touch
+               SUB_SetFade(e, time + autocvar_g_monsters_drop_time, 1);
+               self = oldself;
        }
 }
 
@@ -56,10 +49,10 @@ float monster_isvalidtarget (entity targ, entity ent)
        if(targ == ent)
                return FALSE; // don't attack ourselves
 
-       traceline(ent.origin, targ.origin, MOVE_NORMAL, ent);
+       //traceline(ent.origin, targ.origin, MOVE_NORMAL, ent);
 
-       if(trace_ent != targ)
-               return FALSE;
+       //if(trace_ent != targ)
+               //return FALSE;
 
        if(targ.vehicle_flags & VHF_ISVEHICLE)
        if(!((get_monsterinfo(ent.monsterid)).spawnflags & MON_FLAG_RANGED))
@@ -68,9 +61,6 @@ float monster_isvalidtarget (entity targ, entity ent)
        if(time < game_starttime)
                return FALSE; // monsters do nothing before the match has started
 
-       if(vlen(targ.origin - ent.origin) >= ent.target_range)
-               return FALSE; // enemy is too far away
-
        if(targ.takedamage == DAMAGE_NO)
                return FALSE; // enemy can't be damaged
 
@@ -107,7 +97,7 @@ float monster_isvalidtarget (entity targ, entity ent)
        if (targ.freezetag_frozen)
                return FALSE; // ignore frozen
 
-       if(autocvar_g_monsters_target_infront || ent.spawnflags & MONSTERFLAG_INFRONT)
+       if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT))
        if(ent.enemy != targ)
        {
                float dot;
@@ -128,6 +118,7 @@ entity FindTarget (entity ent)
 
        entity head, closest_target = world;
        head = findradius(ent.origin, ent.target_range);
+       //head = WarpZone_FindRadius(ent.origin, ent.target_range, TRUE);
 
        while(head) // find the closest acceptable target to pass to
        {
@@ -136,12 +127,16 @@ entity FindTarget (entity ent)
                {
                        // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
                        vector head_center = CENTER_OR_VIEWOFS(head);
+                       //vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
                        vector ent_center = CENTER_OR_VIEWOFS(ent);
 
-                       //if(ctf_CheckPassDirection(head_center, ent_center, ent.v_angle, head.WarpZone_findradius_nearest))
+                       traceline(ent_center, head_center, MOVE_NORMAL, ent);
+
+                       if(trace_ent == head)
                        if(closest_target)
                        {
                                vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
+                               //vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target));
                                if(vlen(ent_center - head_center) < vlen(ent_center - closest_target_center))
                                        { closest_target = head; }
                        }
@@ -279,7 +274,8 @@ void UpdateMonsterSounds()
 
 void MonsterSound(.string samplefield, float sound_delay, float delaytoo, float chan)
 {
-       if(delaytoo && time < self.msound_delay)
+       if(delaytoo)
+       if(time < self.msound_delay)
                return; // too early
        GlobalSound(self.samplefield, chan, VOICETYPE_PLAYERSOUND);
 
@@ -357,14 +353,23 @@ float Monster_CanRespawn(entity ent)
        return TRUE;
 }
 
+float Monster_CheckAppearFlags(entity ent, float monster_id);
+float monster_initialize(float mon_id);
+void monster_respawn()
+{
+       // is this function really needed?
+       if(!Monster_CheckAppearFlags(self, self.monsterid))
+               monster_initialize(self.monsterid);
+}
+
 void Monster_Fade ()
 {
        if(Monster_CanRespawn(self))
        {
                self.spawnflags |= MONSTERFLAG_RESPAWNED;
-               self.think = self.monster_spawnfunc;
+               self.think = monster_respawn;
                self.nextthink = time + self.respawntime;
-               self.ltime = 0;
+               self.monster_lifetime = 0;
                self.deadflag = DEAD_RESPAWNING;
                if(self.spawnflags & MONSTER_RESPAWN_DEATHPOINT)
                {
@@ -472,10 +477,40 @@ vector monster_pickmovetarget(entity targ)
        // enemy is always preferred target
        if(self.enemy)
        {
-               makevectors(self.angles);
+               vector targ_origin = ((self.enemy.absmin + self.enemy.absmax) * 0.5);
+               targ_origin = WarpZone_RefSys_TransformOrigin(self.enemy, self, targ_origin); // origin of target as seen by the monster (us)
+               WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
+               
+               if((self.enemy == world)
+                       || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
+                       || (self.enemy.freezetag_frozen)
+                       || (self.enemy.flags & FL_NOTARGET)
+                       || (self.enemy.alpha < 0.5)
+                       || (self.enemy.takedamage == DAMAGE_NO)
+                       || (vlen(self.origin - targ_origin) > self.target_range)
+                       || ((trace_fraction < 1) && (trace_ent != self.enemy)))
+                       //|| (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit)) // TODO: chase timelimit?
+               {
+                       self.enemy = world;
+                       self.pass_distance = 0;
+               }
+               
+               if(self.enemy)
+               {
+                       /*WarpZone_TrailParticles(world, particleeffectnum("red_pass"), self.origin, targ_origin);
+                       print("Trace origin: ", vtos(targ_origin), "\n");
+                       print("Target origin: ", vtos(self.enemy.origin), "\n");
+                       print("My origin: ", vtos(self.origin), "\n"); */
+                       
+                       self.monster_movestate = MONSTER_MOVE_ENEMY;
+                       self.last_trace = time + 1.2;
+                       return targ_origin;
+               }
+       
+               /*makevectors(self.angles);
                self.monster_movestate = MONSTER_MOVE_ENEMY;
                self.last_trace = time + 1.2;
-               return self.enemy.origin;
+               return self.enemy.origin; */
        }
 
        switch(self.monster_moveflags)
@@ -503,31 +538,67 @@ vector monster_pickmovetarget(entity targ)
                {
                        vector pos;
                        self.monster_movestate = MONSTER_MOVE_WANDER;
-                       self.last_trace = time + 2;
-
-                       self.angles_y = rint(random() * 500);
-                       makevectors(self.angles);
-                       pos = self.origin + v_forward * 600;
-
-                       if(self.flags & FL_FLY || self.flags & FL_SWIM)
-                       if(self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
-                       {
-                               pos_z = random() * 200;
-                               if(random() >= 0.5)
-                                       pos_z *= -1;
-                       }
 
                        if(targ)
                        {
                                self.last_trace = time + 0.5;
                                pos = targ.origin;
                        }
+                       else
+                       {
+                               self.last_trace = time + self.wander_delay;
+
+                               self.angles_y = rint(random() * 500);
+                               makevectors(self.angles);
+                               pos = self.origin + v_forward * self.wander_distance;
+
+                               if((self.flags & FL_FLY) || (self.flags & FL_SWIM))
+                               if(self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
+                               {
+                                       pos_z = random() * 200;
+                                       if(random() >= 0.5)
+                                               pos_z *= -1;
+                               }
+                       }
 
                        return pos;
                }
        }
 }
 
+void monster_CalculateVelocity(entity mon, 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, (current_distance / self.pass_distance)));
+       //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
+
+       vector targpos;
+       if(current_height) // make sure we can actually do this arcing path
+       {
+               targpos = (to + ('0 0 1' * current_height));
+               WarpZone_TraceLine(mon.origin, targpos, MOVE_NOMONSTERS, mon);
+               if(trace_fraction < 1)
+               {
+                       //print("normal arc line failed, trying to find new pos...");
+                       WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, mon);
+                       targpos = (trace_endpos + '0 0 -10');
+                       WarpZone_TraceLine(mon.origin, targpos, MOVE_NOMONSTERS, mon);
+                       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; }
+
+       //mon.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y));
+
+       vector desired_direction = normalize(targpos - from);
+       if(turnrate) { mon.velocity = (normalize(normalize(mon.velocity) + (desired_direction * 50)) * movespeed); }
+       else { mon.velocity = (desired_direction * movespeed); }
+       
+       mon.angles = vectoangles(mon.velocity);
+}
+
 void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle)
 {
        fixedmakevectors(self.angles);
@@ -581,7 +652,7 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
        monster_speed_run = runspeed;
        monster_speed_walk = walkspeed;
 
-       if(MUTATOR_CALLHOOK(MonsterMove) || gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
+       if(MUTATOR_CALLHOOK(MonsterMove) || gameover || self.draggedby != world || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
        {
                runspeed = walkspeed = 0;
                if(time >= self.spawn_time)
@@ -605,22 +676,23 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
        if(DIFF_TEAM(self.monster_owner, self))
                self.monster_owner = world;
 
-       if(self.enemy && self.enemy.health < 1)
-               self.enemy = world; // enough!
-
        if(time >= self.last_enemycheck)
        {
-               if(!monster_isvalidtarget(self.enemy, self))
-                       self.enemy = world;
-
                if(!self.enemy)
                {
                        self.enemy = FindTarget(self);
                        if(self.enemy)
+                       {
+                               WarpZone_RefSys_Copy(self.enemy, self);
+                               WarpZone_RefSys_AddInverse(self.enemy, self); // wz1^-1 ... wzn^-1 receiver
+                               self.moveto = WarpZone_RefSys_TransformOrigin(self.enemy, self, (0.5 * (self.enemy.absmin + self.enemy.absmax)));
+                               
+                               self.pass_distance = vlen((('1 0 0' * self.enemy.origin_x) + ('0 1 0' * self.enemy.origin_y)) - (('1 0 0' *  self.origin_x) + ('0 1 0' *  self.origin_y)));
                                MonsterSound(monstersound_sight, 0, FALSE, CH_VOICE);
+                       }
                }
 
-               self.last_enemycheck = time + 0.5;
+               self.last_enemycheck = time + 1; // check for enemies every second
        }
 
        if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single)
@@ -633,49 +705,32 @@ void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_
        if(!self.enemy)
                MonsterSound(monstersound_idle, 7, TRUE, CH_VOICE);
 
-       if(self.state != MONSTER_STATE_ATTACK_LEAP && self.state != MONSTER_STATE_ATTACK_MELEE)
-               self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
        if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND))
        {
                self.state = 0;
                self.touch = MonsterTouch;
        }
 
-       //self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
-       float turny = 0;
-       vector real_angle = vectoangles(self.steerto) - self.angles;
-
-       if(self.state != MONSTER_STATE_ATTACK_LEAP && self.state != MONSTER_STATE_ATTACK_MELEE)
-               turny = 20;
-
-       if(self.flags & FL_SWIM)
-               turny = vlen(self.angles - self.moveto);
-
-       if(turny)
-       {
-               turny = bound(turny * -1, shortangle_f(real_angle_y, self.angles_y), turny);
-               self.angles_y += turny;
-       }
-
        if(self.state == MONSTER_STATE_ATTACK_MELEE)
                self.moveto = self.origin;
 
        if(self.enemy && self.enemy.vehicle)
                runspeed = 0;
 
-       if(((self.flags & FL_FLY) || (self.flags & FL_SWIM)) && self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
+       if(((self.flags & FL_FLY) || (self.flags & FL_SWIM)) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL))
                v_forward = normalize(self.moveto - self.origin);
        else
                self.moveto_z = self.origin_z;
 
        if(vlen(self.origin - self.moveto) > 64)
        {
-               if(self.flags & FL_FLY || self.flags & FL_SWIM)
+               if((self.flags & FL_ONGROUND) || ((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
+                       monster_CalculateVelocity(self, self.moveto, self.origin, TRUE, ((self.enemy) ? runspeed : walkspeed));
+               
+               /*&if(self.flags & FL_FLY || self.flags & FL_SWIM)
                        movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
                else
-                       movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
+                       movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); */
 
                if(time > self.pain_finished)
                if(time > self.attack_finished_single)
@@ -723,8 +778,8 @@ void monster_dead_think()
 
        CSQCMODEL_AUTOUPDATE();
 
-       if(self.ltime != 0)
-       if(time >= self.ltime)
+       if(self.monster_lifetime != 0)
+       if(time >= self.monster_lifetime)
        {
                Monster_Fade();
                return;
@@ -740,16 +795,17 @@ void monsters_setstatus()
 void Monster_Appear()
 {
        self.enemy = activator;
-       self.spawnflags &= ~MONSTERFLAG_APPEAR;
-       self.monster_spawnfunc();
+       //self.spawnflags &= ~MONSTERFLAG_APPEAR;
+       monster_initialize(self.monsterid);
 }
 
-float Monster_CheckAppearFlags(entity ent)
+float Monster_CheckAppearFlags(entity ent, float monster_id)
 {
        if(!(ent.spawnflags & MONSTERFLAG_APPEAR))
                return FALSE;
 
        ent.think = func_null;
+       ent.monsterid = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used)
        ent.nextthink = 0;
        ent.use = Monster_Appear;
        ent.flags = FL_MONSTER; // set so this monster can get butchered
@@ -788,6 +844,7 @@ void monsters_corpse_damage (entity inflictor, entity attacker, float damage, fl
 
                self.think = SUB_Remove;
                self.nextthink = time + 0.1;
+               self.event_damage = func_null;
        }
 }
 
@@ -795,7 +852,7 @@ void monster_die(entity attacker, float gibbed)
 {
        self.think = monster_dead_think;
        self.nextthink = time;
-       self.ltime = time + 5;
+       self.monster_lifetime = time + 5;
 
        monster_dropitem();
 
@@ -820,7 +877,7 @@ void monster_die(entity attacker, float gibbed)
        if(self.candrop && self.weapon)
                W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
 
-       self.event_damage       = monsters_corpse_damage;
+       self.event_damage       = ((gibbed) ? func_null : monsters_corpse_damage);
        self.solid                      = SOLID_CORPSE;
        self.takedamage         = DAMAGE_AIM;
        self.deadflag           = DEAD_DEAD;
@@ -840,12 +897,18 @@ void monster_die(entity attacker, float gibbed)
 
 void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
 {
+       if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
+               return;
+
        if(time < self.pain_finished && deathtype != DEATH_KILL)
                return;
 
        if(time < self.spawnshieldtime && deathtype != DEATH_KILL)
                return;
 
+       if(deathtype == DEATH_FALL && self.draggedby != world)
+               return;
+
        vector v;
        float take, save;
 
@@ -930,8 +993,8 @@ void monster_think()
        self.think = monster_think;
        self.nextthink = self.ticrate;
 
-       if(self.ltime)
-       if(time >= self.ltime)
+       if(self.monster_lifetime)
+       if(time >= self.monster_lifetime)
        {
                Damage(self, self, self, self.health + self.max_health, DEATH_KILL, self.origin, self.origin);
                return;
@@ -965,6 +1028,9 @@ float monster_spawn()
        if(!self.attack_range)
                self.attack_range = autocvar_g_monsters_attack_range;
 
+       if(!self.wander_delay) { self.wander_delay = 2; }
+       if(!self.wander_distance) { self.wander_distance = 600; }
+
        precache_monstersounds();
        UpdateMonsterSounds();
 
@@ -974,8 +1040,11 @@ float monster_spawn()
        MonsterSound(monstersound_spawn, 0, FALSE, CH_VOICE);
 
        WaypointSprite_Spawn(M_NAME(self.monsterid), 0, 1024, self, '0 0 1' * (self.maxs_z + 15), world, self.team, self, sprite, TRUE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0'));
-       WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
-       WaypointSprite_UpdateHealth(self.sprite, self.health);
+       if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE))
+       {
+               WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+               WaypointSprite_UpdateHealth(self.sprite, self.health);
+       }
 
        self.think = monster_think;
        self.nextthink = time + self.ticrate;
@@ -986,7 +1055,7 @@ float monster_spawn()
        return TRUE;
 }
 
-float monster_initialize(float mon_id, float nodrop)
+float monster_initialize(float mon_id)
 {
        if(!autocvar_g_monsters)
                return FALSE;
@@ -1005,7 +1074,7 @@ float monster_initialize(float mon_id, float nodrop)
                self.team = 0;
 
        if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) // naturally spawned monster
-       if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
+       if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either
                monsters_total += 1;
 
        setmodel(self, mon.model);
@@ -1035,14 +1104,18 @@ float monster_initialize(float mon_id, float nodrop)
        self.candrop                    = TRUE;
        self.view_ofs                   = '0 0 1' * (self.maxs_z * 0.5);
        self.oldtarget2                 = self.target2;
+       self.pass_distance              = 0;
        self.deadflag                   = DEAD_NO;
        self.scale                              = 1;
-       self.noalign                    = nodrop;
+       self.noalign                    = (mon.spawnflags & MONSTER_TYPE_FLY);
        self.spawn_time                 = time;
        self.spider_slowness    = 0;
        self.gravity                    = 1;
        self.dphitcontentsmask  = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
 
+       if(autocvar_g_monsters_edit)
+               self.grab = 1; // owner may carry their monster
+
        if(autocvar_g_fullbrightplayers)
                self.effects |= EF_FULLBRIGHT;
 
index 65331207279af26037e6a9084253e4cb7d8ebc45..e8759b37592e175a29fcda17a4d86538c78f0e23 100644 (file)
@@ -11,6 +11,11 @@ float monsters_killed;
 void monsters_setstatus(); // monsters.qc
 .float monster_moveflags; // checks where to move when not attacking
 
+.float wander_delay;
+.float wander_distance;
+
+.float monster_lifetime;
+
 .float spider_slowness; // special spider timer
 
 void monster_remove(entity mon); // removes a monster
@@ -69,11 +74,10 @@ const float MONSTERFLAG_NORESPAWN = 4;
 const float MONSTERFLAG_FLY_VERTICAL = 8; // fly/swim vertically
 const float MONSTERFLAG_INFRONT = 32; // only check for enemies infront of us
 const float MONSTERFLAG_MINIBOSS = 64; // monster spawns as mini-boss (also has a chance of naturally becoming one)
+const float MONSTERFLAG_INVINCIBLE = 128; // monster doesn't take damage (may be used for map objects & temporary monsters)
 const float MONSTERFLAG_SPAWNED = 16384; // flag for spawned monsters
 const float MONSTERFLAG_RESPAWNED = 32768; // flag for re-spawned monsters
 
-.void() monster_spawnfunc;
-
 .float monster_movestate; // used to tell what the monster is currently doing
 const float MONSTER_MOVE_OWNER = 1; // monster will move to owner if in range, or stand still
 const float MONSTER_MOVE_WANDER = 2; // monster will ignore owner & wander around
index c8c87e09687f49a6df6e356acd6e58eaf9cd2255..06ee52992696dac3a45286d07fd2301ab8d0e433 100644 (file)
@@ -283,6 +283,7 @@ void ClientCommand_mobspawn(float request, float argc)
                        else if(MUTATOR_CALLHOOK(AllowMobSpawning)) { sprint(self, "Monster spawning is currently disabled by a mutator.\n"); return; }
                        else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); return; }
                        else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); return; }
+                       else if(self.freezetag_frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
                        else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); return; }
                        else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); return; }
                        else if(self.monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
@@ -302,7 +303,7 @@ void ClientCommand_mobspawn(float request, float argc)
                                        }
                                }
 
-                               if(found)
+                               if(found || tospawn == "random")
                                {
                                        self.monstercount += 1;
                                        totalspawned += 1;
@@ -311,7 +312,7 @@ void ClientCommand_mobspawn(float request, float argc)
                                        WarpZone_TraceBox (CENTER_OR_VIEWOFS(self), PL_MIN, PL_MAX, CENTER_OR_VIEWOFS(self) + v_forward * 150, TRUE, self);
                                        //WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 150, MOVE_NORMAL, self);
                                
-                                       e = spawnmonster(tospawn, 0, self, self, trace_endpos, FALSE, moveflag);
+                                       e = spawnmonster(tospawn, 0, self, self, trace_endpos, FALSE, FALSE, moveflag);
                                        
                                        sprint(self, strcat("Spawned ", e.monster_name, "\n"));
                                        
index 5acc70388804c45c22d8002d695bb8453d05cd7f..1242ecbd9e3f0cfb91598eb9ed5fe9c0f9aacf2b 100644 (file)
@@ -51,7 +51,9 @@ void invasion_SpawnChosenMonster(float mon)
                return;
        }
 
-       monster = spawnmonster("", mon, spawn_point, spawn_point, spawn_point.origin, FALSE, 2);
+       monster = spawnmonster("", mon, spawn_point, spawn_point, spawn_point.origin, FALSE, FALSE, 2);
+       
+       monster.target2 = spawn_point.target2;
 
        if(inv_roundcnt >= inv_maxrounds)
                monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses
@@ -157,13 +159,13 @@ void Invasion_RoundStart()
 
        inv_roundcnt += 1;
 
-       inv_monsterskill = inv_roundcnt + (numplayers * 0.3);
+       inv_monsterskill = inv_roundcnt + max(1, numplayers * 0.3);
 
        inv_maxcurrent = 0;
        inv_numspawned = 0;
        inv_numkilled = 0;
 
-       inv_maxspawned = rint(min(autocvar_g_invasion_monster_count, autocvar_g_invasion_monster_count * (inv_roundcnt * 0.5)));
+       inv_maxspawned = rint(max(autocvar_g_invasion_monster_count, autocvar_g_invasion_monster_count * (inv_roundcnt * 0.5)));
 }
 
 MUTATOR_HOOKFUNCTION(invasion_MonsterDies)