seta g_race_laps_limit -1 "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
-seta g_invasion_round_limit -1 "Invasion round limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+seta g_invasion_point_limit -1 "Invasion point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
// =================================
set g_monsters_teams 1
set g_monsters_score_kill 0
set g_monsters_score_spawned 0
+set g_monsters_sounds 1
set g_monsters_spawnshieldtime 2
set g_monsters_typefrag 1
set g_monsters_target_range 2000
REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,"timelimit=20 pointlimit=30");
#define g_keepaway IS_GAMETYPE(KEEPAWAY)
-REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=5");
+REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,"pointlimit=50");
#define g_invasion IS_GAMETYPE(INVASION)
const float MAPINFO_FEATURE_WEAPONS = 1; // not defined for minstagib-only maps
{
self.classname = "monster_mage";
- if(Monster_CheckAppearFlags(self, MON_MAGE))
- return;
-
if(!monster_initialize(MON_MAGE)) { remove(self); return; }
}
}
case MR_PRECACHE:
{
- precache_model ("models/monsters/mage.dpm");
precache_sound ("weapons/grenade_impact.wav");
precache_sound ("weapons/tagexp1.wav");
return TRUE;
{
case MR_PRECACHE:
{
- precache_model ("models/monsters/mage.dpm");
return TRUE;
}
}
{
self.classname = "monster_shambler";
- if(Monster_CheckAppearFlags(self, MON_SHAMBLER))
- return;
-
if(!monster_initialize(MON_SHAMBLER)) { remove(self); return; }
}
}
case MR_PRECACHE:
{
- precache_model ("models/monsters/shambler.mdl");
return TRUE;
}
}
{
case MR_PRECACHE:
{
- precache_model ("models/monsters/shambler.mdl");
return TRUE;
}
}
pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
RadiusDamage(self, self.realowner, 0, 0, 25, world, 25, self.projectiledeathtype, world);
- for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0)
+ for(e = findradius(self.origin, 25); e; e = e.chain) if(e != self) if(e.takedamage && e.deadflag == DEAD_NO) if(e.health > 0) if(e.monsterid != MON_SPIDER)
e.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime);
remove(self);
{
self.classname = "monster_spider";
- if(Monster_CheckAppearFlags(self, MON_SPIDER))
- return;
-
if(!monster_initialize(MON_SPIDER)) { remove(self); return; }
}
}
case MR_PRECACHE:
{
- precache_model ("models/monsters/spider.dpm");
precache_sound ("weapons/electro_fire2.wav");
return TRUE;
}
{
case MR_PRECACHE:
{
- precache_model ("models/monsters/spider.dpm");
return TRUE;
}
}
{
self.classname = "monster_wyvern";
- if(Monster_CheckAppearFlags(self, MON_WYVERN))
- return;
-
if(!monster_initialize(MON_WYVERN)) { remove(self); return; }
}
}
case MR_PRECACHE:
{
- precache_model ("models/monsters/wizard.mdl");
return TRUE;
}
}
{
case MR_PRECACHE:
{
- precache_model ("models/monsters/wizard.mdl");
return TRUE;
}
}
{
self.classname = "monster_zombie";
- if(Monster_CheckAppearFlags(self, MON_ZOMBIE))
- return;
-
if(!monster_initialize(MON_ZOMBIE)) { remove(self); return; }
}
}
case MR_PRECACHE:
{
- precache_model ("models/monsters/zombie.dpm");
return TRUE;
}
}
{
case MR_PRECACHE:
{
- precache_model ("models/monsters/zombie.dpm");
return TRUE;
}
}
e.model = strzone(strcat("models/monsters/", modelname));
#ifndef MENUQC
+ precache_model(e.model);
func(MR_PRECACHE);
#endif
}
if(m)
return m;
return dummy_monster_info;
-}
\ No newline at end of file
+}
void MonsterSound(.string samplefield, float sound_delay, float delaytoo, float chan)
{
+ if(!autocvar_g_monsters_sounds) { return; }
+
if(delaytoo)
if(time < self.msound_delay)
return; // too early
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);
+ monster_initialize(self.monsterid);
}
void Monster_Fade ()
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)
+ if(((self.flags & FL_FLY) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL)) || (self.flags & FL_SWIM))
{
pos_z = random() * 200;
if(random() >= 0.5)
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);
+
+ //mon.steerto = steerlib_attract2(targpos, 0.5, 500, 0.95);
+ //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);
+ //fixedmakevectors(self.angles);
if(self.target2)
self.goalentity = find(world, targetname, self.target2);
if(self.enemy && self.enemy.vehicle)
runspeed = 0;
- if(((self.flags & FL_FLY) || (self.flags & FL_SWIM)) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL))
- v_forward = normalize(self.moveto - self.origin);
- else
+ if(!(((self.flags & FL_FLY) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL)) || (self.flags & FL_SWIM)))
+ //v_forward = normalize(self.moveto - self.origin);
+ //else
self.moveto_z = self.origin_z;
if(vlen(self.origin - self.moveto) > 64)
if (vlen(self.velocity) <= 30)
self.frame = manim_idle;
}
+
+ self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+ vector real_angle = vectoangles(self.steerto) - self.angles;
+ float turny = 25;
+ if(self.state == MONSTER_STATE_ATTACK_MELEE)
+ turny = 0;
+ if(turny)
+ {
+ turny = bound(turny * -1, shortangle_f(real_angle_y, self.angles_y), turny);
+ self.angles_y += turny;
+ }
monster_checkattack(self, self.enemy);
}
void Monster_Appear()
{
self.enemy = activator;
- //self.spawnflags &= ~MONSTERFLAG_APPEAR;
+ self.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
monster_initialize(self.monsterid);
}
self.state = 0;
self.attack_finished_single = 0;
- if(!(self.flags & FL_FLY))
+ if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
self.velocity = '0 0 0';
MON_ACTION(self.monsterid, MR_DEATH);
float monster_initialize(float mon_id)
{
- if(!autocvar_g_monsters)
- return FALSE;
+ if(!autocvar_g_monsters) { return FALSE; }
+
+ if(Monster_CheckAppearFlags(self, mon_id)) { return TRUE; } // return true so the monster isn't removed
entity mon = get_monsterinfo(mon_id);
monsters_total += 1;
setmodel(self, mon.model);
- setsize(self, mon.mins, mon.maxs);
+ //setsize(self, mon.mins, mon.maxs);
self.flags = FL_MONSTER;
self.takedamage = DAMAGE_AIM;
self.bot_attack = TRUE;
self.oldtarget2 = self.target2;
self.pass_distance = 0;
self.deadflag = DEAD_NO;
- self.scale = 1;
- self.noalign = (mon.spawnflags & MONSTER_TYPE_FLY);
+ self.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM));
self.spawn_time = time;
self.spider_slowness = 0;
self.gravity = 1;
self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
+ if(!self.scale)
+ self.scale = 1;
+
if(autocvar_g_monsters_edit)
self.grab = 1; // owner may carry their monster
}
if(mon.spawnflags & MONSTER_SIZE_BROKEN)
- self.scale = 1.3;
+ if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
+ self.scale *= 1.3;
+
+ setsize(self, mon.mins * self.scale, mon.maxs * self.scale);
if(!self.ticrate)
self.ticrate = autocvar_g_monsters_think_delay;
float autocvar_g_physical_items_reset;
float autocvar_g_monsters;
float autocvar_g_monsters_edit;
+float autocvar_g_monsters_sounds;
float autocvar_g_monsters_think_delay;
float autocvar_g_monsters_max;
float autocvar_g_monsters_max_perplayer;
float autocvar_g_touchexplode_edgedamage;
float autocvar_g_touchexplode_force;
float autocvar_g_invasion_round_timelimit;
-#define autocvar_g_invasion_round_limit cvar("g_invasion_round_limit")
+#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
float autocvar_g_invasion_warmup;
float autocvar_g_invasion_monster_count;
float autocvar_g_invasion_zombies_only;
RandomSelection_Init();
for(e = world;(e = find(e, classname, "invasion_spawnpoint")); )
- RandomSelection_Add(e, 0, string_null, 1, 1);
+ {
+ if(time >= e.spawnshieldtime)
+ {
+ e.spawnshieldtime = time + 2;
+ RandomSelection_Add(e, 0, string_null, 1, 1);
+ }
+ }
return RandomSelection_chosen_ent;
}
return;
}
- monster = spawnmonster("", mon, spawn_point, spawn_point, spawn_point.origin, FALSE, FALSE, 2);
+ monster = spawnmonster("", ((spawn_point.monsterid) ? spawn_point.monsterid : mon), spawn_point, spawn_point, spawn_point.origin, FALSE, FALSE, 2);
monster.target2 = spawn_point.target2;
+ monster.spawnshieldtime = time;
if(inv_roundcnt >= inv_maxrounds)
monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses
FOR_EACH_MONSTER(head)
monster_remove(head);
- if(inv_roundcnt >= inv_maxrounds)
- {
- NextLevel();
- return 1;
- }
-
Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_ROUND_OVER);
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ROUND_OVER);
round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit);
++total_alive_monsters;
}
- if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < 10) // 10 at a time should be plenty
+ if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned)
{
if(time >= inv_lastcheck)
{
if(inv_numspawned < 1 || inv_numkilled < inv_maxspawned)
return 0; // nothing has spawned yet, or there are still alive monsters
- if(inv_roundcnt >= inv_maxrounds)
- {
- NextLevel();
- return 1;
- }
-
entity winner = world;
float winning_score = 0;
++numplayers;
}
- inv_roundcnt += 1;
+ if(inv_roundcnt < inv_maxrounds)
+ inv_roundcnt += 1; // a limiter to stop crazy counts
inv_monsterskill = inv_roundcnt + max(1, numplayers * 0.3);
MUTATOR_HOOKFUNCTION(invasion_MonsterSpawn)
{
if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
- {
- monster_remove(self);
- return FALSE;
- }
+ return TRUE;
if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
{
return FALSE;
}
+MUTATOR_HOOKFUNCTION(invasion_OnEntityPreSpawn)
+{
+ if(startsWith(self.classname, "monster_"))
+ if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
+ return TRUE;
+
+ return FALSE;
+}
+
MUTATOR_HOOKFUNCTION(invasion_PlayerThink)
{
monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned
allowed_to_spawn = TRUE;
inv_roundcnt = 0;
+ inv_maxrounds = 15; // 15?
}
MUTATOR_DEFINITION(gamemode_invasion)
{
MUTATOR_HOOK(MonsterDies, invasion_MonsterDies, CBC_ORDER_ANY);
MUTATOR_HOOK(MonsterSpawn, invasion_MonsterSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(OnEntityPreSpawn, invasion_OnEntityPreSpawn, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPreThink, invasion_PlayerThink, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerRegen, invasion_PlayerRegen, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, invasion_PlayerSpawn, CBC_ORDER_ANY);
if(g_invasion)
{
- timelimit_override = 0; // no timelimit in invasion, round based
- fraglimit_override = autocvar_g_invasion_round_limit;
+ fraglimit_override = autocvar_g_invasion_point_limit;
MUTATOR_ADD(gamemode_invasion);
}
else
g_race_qualifying = 0;
}
-
- if(g_invasion)
- {
- inv_maxrounds = cvar("fraglimit");
- cvar_set("fraglimit", "0");
- }
if(g_race || g_cts)
{