MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH; // DM always works
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RUNEMATCH; // Rune always works
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS; // LMS always works
+ MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY; // Keepaway always works
if(spawnpoints >= 8 && diameter > 4096) {
MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH;
case MAPINFO_TYPE_ONSLAUGHT: return "20 0";
case MAPINFO_TYPE_NEXBALL: return "5 20 0";
case MAPINFO_TYPE_CTS: return "20 0 0";
+ case MAPINFO_TYPE_KEEPAWAY: return "30 20 0";
default: return "";
}
}
s = cdr(s);
}
+ if(pWantedType == MAPINFO_TYPE_KEEPAWAY)
+ {
+ sa = car(s);
+ if(sa != "")
+ cvar_set("fraglimit", sa);
+ s = cdr(s)
+ }
+
// rc = timelimit timelimit_qualification laps laps_teamplay
if(pWantedType == MAPINFO_TYPE_RACE)
{
case MAPINFO_TYPE_ONSLAUGHT: return "timelimit=20";
case MAPINFO_TYPE_NEXBALL: return "timelimit=20 pointlimit=5 leadlimit=0";
case MAPINFO_TYPE_CTS: return "timelimit=20 skill=-1";
+ case MAPINFO_TYPE_KEEPAWAY: return "timelimit=20 pointlimit=30";
default: return "";
}
}
else if(t == "rc") return MAPINFO_TYPE_RACE;
else if(t == "nexball") return MAPINFO_TYPE_NEXBALL;
else if(t == "cts") return MAPINFO_TYPE_CTS;
+ else if(t == "ka") return MAPINFO_TYPE_KEEPAWAY;
else if(t == "all") return MAPINFO_TYPE_ALL;
else return 0;
}
else if(t == MAPINFO_TYPE_RACE) return "rc";
else if(t == MAPINFO_TYPE_NEXBALL) return "nexball";
else if(t == MAPINFO_TYPE_CTS) return "cts";
+ else if(t == MAPINFO_TYPE_KEEPAWAY) return "ka";
else if(t == MAPINFO_TYPE_ALL) return "all";
else return "";
}
return MAPINFO_TYPE_NEXBALL;
else if(cvar("g_cts"))
return MAPINFO_TYPE_CTS;
+ else if(cvar("g_ka"))
+ return MAPINFO_TYPE_KEEPAWAY;
else
return MAPINFO_TYPE_DEATHMATCH;
}
{
switch(t)
{
- case MAPINFO_TYPE_DEATHMATCH: return "g_dm";
- case MAPINFO_TYPE_TEAM_DEATHMATCH: return "g_tdm";
- case MAPINFO_TYPE_DOMINATION: return "g_domination";
- case MAPINFO_TYPE_CTF: return "g_ctf";
- case MAPINFO_TYPE_RUNEMATCH: return "g_runematch";
- case MAPINFO_TYPE_LMS: return "g_lms";
- case MAPINFO_TYPE_ARENA: return "g_arena";
- case MAPINFO_TYPE_CA: return "g_ca";
- case MAPINFO_TYPE_KEYHUNT: return "g_kh";
- case MAPINFO_TYPE_ASSAULT: return "g_assault";
- case MAPINFO_TYPE_ONSLAUGHT: return "g_onslaught";
- case MAPINFO_TYPE_RACE: return "g_race";
- case MAPINFO_TYPE_NEXBALL: return "g_nexball";
- case MAPINFO_TYPE_CTS: return "g_cts";
+ case MAPINFO_TYPE_DEATHMATCH: return "g_dm";
+ case MAPINFO_TYPE_TEAM_DEATHMATCH: return "g_tdm";
+ case MAPINFO_TYPE_DOMINATION: return "g_domination";
+ case MAPINFO_TYPE_CTF: return "g_ctf";
+ case MAPINFO_TYPE_RUNEMATCH: return "g_runematch";
+ case MAPINFO_TYPE_LMS: return "g_lms";
+ case MAPINFO_TYPE_ARENA: return "g_arena";
+ case MAPINFO_TYPE_CA: return "g_ca";
+ case MAPINFO_TYPE_KEYHUNT: return "g_kh";
+ case MAPINFO_TYPE_ASSAULT: return "g_assault";
+ case MAPINFO_TYPE_ONSLAUGHT: return "g_onslaught";
+ case MAPINFO_TYPE_RACE: return "g_race";
+ case MAPINFO_TYPE_NEXBALL: return "g_nexball";
+ case MAPINFO_TYPE_CTS: return "g_cts";
+ case MAPINFO_TYPE_KEEPAWAY: return "g_ka";
default: return "";
}
}
cvar_set("g_race", (t == MAPINFO_TYPE_RACE) ? "1" : "0");
cvar_set("g_nexball", (t == MAPINFO_TYPE_NEXBALL) ? "1" : "0");
cvar_set("g_cts", (t == MAPINFO_TYPE_CTS) ? "1" : "0");
+ cvar_set("g_ka", (t == MAPINFO_TYPE_KEEPAWAY) ? "1" : "0");
}
void MapInfo_LoadMap(string s)
--- /dev/null
+void ka_Initialize()
+{
+ if(!g_keepaway) {
+ remove(self);
+ return;
+ }
+ if (!self.model) {
+ self.model = "models/nexball/ball.md3";
+ self.scale = 1.3;
+ }
+
+ precache_model(self.model);
+ setmodel(self, self.model);
+ setsize(self, BALL_MINS, BALL_MAXS);
+ ball_scale = self.scale;
+ self.classname = "keepawayball";
+ self.damageforcescale = cvar("g_keepawayball_damageforcescale");
+ self.effects = self.effects | EF_FULLBRIGHT;
+ self.movetype = MOVETYPE_BOUNCE;
+ self.touch = ka_TouchEvent;
+ self.think = ka_SpawnBall;
+ self.nextthink = time;
+ self.flags = FL_ITEM;
+ self.reset = ka_Reset;
+ self.owner = world;
+
+ // todo: Waypoints and radar
+ //WaypointSprite_AttachCarrier();
+}
+
+void ka_SpawnBall()
+{
+ if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
+ {
+ makevectors(self.angles);
+ self.movetype = MOVETYPE_BOUNCE;
+ self.velocity = '0 0 200';
+ self.angles = '0 0 0';
+ self.solid = SOLID_TRIGGER;
+ //self.touch = ka_TouchEvent;
+ self.think = ka_SpawnBall;
+ self.nextthink = time + cvar("g_keepawayball_respawntime");
+ }
+ else
+ {
+ // sorry, can't spawn, better luck next frame
+ self.think = ka_SpawnBall;
+ self.nextthink = time;
+ }
+}
+
+void ka_TouchEvent(entity plyr)
+{
+ if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)
+ {
+ self.think = ka_SpawnBall;
+ self.nextthink = time;
+ return;
+ }
+ if (!plyr)
+ return;
+ if (!self)
+ return;
+ if ((other.classname != "player" || other.health < 1) && (time > self.ctf_droptime + cvar("g_keepawayball_respawntime")))
+ return;
+ if (self.wait > time)
+ return;
+
+ self.owner = other;
+ other.ballcarried = self;
+ setattachment(self, other, "");
+ setorigin(self, BALL_ATTACHORG);
+
+ self.velocity = '0 0 0';
+ self.movetype = MOVETYPE_NONE;
+ self.touch = SUB_Null;
+ self.alpha = 0.01;
+
+ self.think = SUB_Null;
+ self.nextthink = 0;
+
+ self.glow_color = cvar("g_keepawayball_trail_color");
+ self.glowtrail = TRUE;
+ plyr.effects |= 8;
+ plyr.alpha = 0.6
+
+ bprint(other.netname, "^7 has picked up the ball!\n");
+ WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
+ WriteString(MSG_BROADCAST, strcat("\n\n", other.netname, "^7 has picked up the ball!\n"));
+ sound(self.owner, CHAN_AUTO, "keepaway/pickedup.wav", VOL_BASE, ATTN_NORM);
+
+ PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1);
+
+ // todo: Waypoints and radar
+}
+
+void ka_DropEvent(entity plyr, entity ball)
+{
+ setattachment(ball, world, "");
+ ball.movetype = MOVETYPE_BOUNCE;
+ ball.solid = SOLID_TRIGGER;
+ ball.wait = time + 1;
+ ball.ctf_droptime = time;
+ ball.think = ka_SpawnBall;
+ ball.nextthink = time + cvar("g_keepawayball_respawntime");
+ ball.touch = ka_TouchEvent;
+ plyr.effects = EF_LOWPRECISION;
+ plyr.alpha = 1.0;
+ ball.alpha = 1.0;
+ setorigin(ball, plyr.origin + '0 0 10');
+ ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom();
+
+ bprint(plyr.netname, "^7 has dropped the ball!\n");
+ WriteByte(MSG_BROADCAST, SVC_CENTERPRINT);
+ WriteString(MSG_BROADCAST, strcat("\n\n", plyr.netname, "^7 has dropped the ball!\n"));
+ sound(other, CHAN_AUTO, "keepaway/dropped.wav", VOL_BASE, ATTN_NORM);
+
+ PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1);
+
+ // todo
+ //WaypointSprite_AttachCarrier("ka-ball", ball);
+ //WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier);
+
+ ball.owner.kaballcarried = world;
+ ball.owner = world;
+}
+
+/*
+void ka_CheckWinner()
+{
+
+}
+
+MUTATOR_HOOKFUNCTION(ka_PlayerDies)
+{
+ float i;
+ entity e;
+
+ float temp_tag_players_count;
+ temp_tag_players_count = tag_players_count;
+
+ if(frag_target.tagcolor == frag_target.tagcolor_original) // if this is the first time we die... (our tagcolor remained unchanged)
+ {
+ for(i = 0; i < temp_tag_players_count; ++i) // check other players...
+ {
+ e = tag_players[i];
+ if(e == world) // empty slot, skip to next
+ {
+ if(temp_tag_players_count < TAGCOLOR_MAX - 1) // just in case
+ ++temp_tag_players_count;
+ continue;
+ }
+
+ if(e.tagcolor == frag_target.tagcolor_original) // and see if they have our original tag color
+ {
+ tag_GetFragAttackers_ColorOwner();
+ centerprint(e, strcat("^1Your master ^7", frag_target.netname, "^1 was tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"));
+ e.tagcolor = frag_attacker.tagcolor; // if so, remove it, our tag color has now "died out" from this round and we can not win anymore. The attacker will "summon" all of our previously fragged targets, and also us.
+ setcolor(e, 16 * e.tagcolor + e.tagcolor);
+ }
+ }
+ }
+ else
+ {
+ frag_target.tagcolor = frag_attacker.tagcolor;
+ setcolor(frag_target, 16 * frag_target.tagcolor + frag_target.tagcolor);
+ }
+
+ tag_GetFragAttackers_ColorOwner();
+
+ if(color_owner_self)
+ color_owner_green = "^2your own";
+ centerprint(frag_attacker, strcat("^2You tagged ^7", frag_target.netname, " ^2with ^7", color_owner_green, " ^2color.\n"));
+
+ if(color_owner_self)
+ color_owner_red = "^1their own";
+ centerprint(frag_target, strcat("^1You were tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n"));
+ bprint("^7", frag_target.netname, "^1 was tagged by ^7", frag_attacker.netname, " ^1with ^7", color_owner_red, " ^1color.\n");
+
+ frag_target.health = cvar("g_balance_health_start"); // "respawn" the player :P
+
+ tag_CheckWinner();
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(tag_RemovePlayer)
+{
+ if(self.tag_playernum == -1)
+ return 0; // nothing to remove
+
+ float i, j;
+ /*for (i = self.tag_playernum; i < tag_players_count; ++i)
+ {
+ tag_players[i] = tag_players[i+1];
+ tag_players[i].tag_playernum = tag_players[i].tag_playernum - 1;
+ }
+ */
+
+ tag_players[self.tag_playernum] = world;
+ tag_players_count = tag_players_count - 1;
+
+ // if other players have our color, randomize their color
+ entity e, random_player;
+ float temp_tag_players_count;
+ temp_tag_players_count = tag_players_count;
+
+ if(!next_round) // ... but ONLY if next_round isn't set. We don't care about the colors if the round has already ended
+ for(i = 0; i < temp_tag_players_count; ++i) // check other players...
+ {
+ e = tag_players[i];
+ if(e == world) // empty slot, skip to next
+ {
+ if(temp_tag_players_count < TAGCOLOR_MAX - 1) // just in case
+ ++temp_tag_players_count;
+ continue;
+ }
+
+ if(e.tagcolor == self.tagcolor_original) // and see if they have our original tag color
+ {
+ for(j = 0; j < 100; ++j) // try 100 times to find a color that isn't the same as our color. If this fails we are either damn unlucky, or there are really only players left of our color
+ {
+ random_player = tag_players[floor(random() * (TAGCOLOR_MAX - 1))];
+
+ if(random_player == world) // hit empty slot, try again
+ continue;
+
+ if(random_player.tagcolor != self.tagcolor_original) // break if we found another color
+ {
+ break;
+ }
+ }
+ e.tagcolor = random_player.tagcolor;
+ setcolor(e, 16 * e.tagcolor + e.tagcolor);
+ }
+ }
+
+ self.tag_playernum = -1;
+
+ if(tag_players_count > 1 && time > warmup)
+ tag_CheckWinner();
+
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(tag_GiveFragsForKill)
+{
+ frag_score = 0; // no frags counted in Tag, maybe later (TODO)
+ return 1;
+}
+
+MUTATOR_HOOKFUNCTION(tag_PlayerPreThink)
+{
+ setcolor(self, 16 * self.tagcolor + self.tagcolor); // prevent cheating by changing player colors
+ return 1;
+}
+*/
+
+
+MUTATOR_DEFINITION(gamemode_ka)
+{
+ MUTATOR_HOOK(MakePlayerObserver, ka_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientDisconnect, ka_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, ka_PlayerDies, CBC_ORDER_ANY);
+ //MUTATOR_HOOK(PlayerSpawn, ka_PlayerSpawn, CBC_ORDER_ANY);
+ //MUTATOR_HOOK(GiveFragsForKill, ka_GiveFragsForKill, CBC_ORDER_FIRST);
+ //MUTATOR_HOOK(PlayerPreThink, ka_PlayerPreThink, CBC_ORDER_FIRST);
+
+ MUTATOR_ONADD
+ {
+ if(time > 1) // game loads at time 1
+ error("This is a game type and it cannot be added at runtime.");
+ g_ka = 1;
+ ka_Initialize();
+ }
+
+ MUTATOR_ONREMOVE
+ {
+ g_ka = 0;
+ error("This is a game type and it cannot be removed at runtime.");
+ }
+
+ return 0;
+}
+