]> git.rm.cloudns.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Single player game mode
authorMattia Basaglia <mattia.basaglia@gmail.com>
Sat, 18 Mar 2017 23:32:05 +0000 (23:32 +0000)
committerMattia Basaglia <mattia.basaglia@gmail.com>
Sat, 18 Mar 2017 23:32:05 +0000 (23:32 +0000)
qcsrc/common/mapinfo.qh
qcsrc/server/g_world.qc
qcsrc/server/mutators/mutator/_mod.inc
qcsrc/server/mutators/mutator/_mod.qh
qcsrc/server/mutators/mutator/gamemode_singleplayer.qc [new file with mode: 0644]
qcsrc/server/mutators/mutator/gamemode_singleplayer.qh [new file with mode: 0644]

index a3e000d33351fde6a8f9edb97808375d434670cd..2958903b89aa4f9802ab77a75adaa065d605716a 100644 (file)
@@ -477,6 +477,24 @@ CLASS(Invasion, Gametype)
 ENDCLASS(Invasion)
 REGISTER_GAMETYPE(INVASION, NEW(Invasion));
 
+CLASS(SinglePlayer, Gametype)
+    INIT(SinglePlayer)
+    {
+        this.gametype_init(this, _("SinglePlayer"),"sp","g_singleplayer",true,"","",_("Explore the map and defeat the enemies"));
+    }
+    METHOD(SinglePlayer, m_isTwoBaseMode, bool())
+    {
+        return true;
+    }
+    METHOD(SinglePlayer, m_generate_mapinfo, void(Gametype this, string v))
+    {
+        if(v == "info_player_singleplayer")
+            MapInfo_Map_supportedGametypes |= this.m_flags;
+    }
+ENDCLASS(SinglePlayer)
+REGISTER_GAMETYPE(SINGLE_PLAYER, NEW(SinglePlayer));
+#define g_singleplayer IS_GAMETYPE(SINGLE_PLAYER)
+
 const int MAPINFO_FEATURE_WEAPONS       = 1; // not defined for instagib-only maps
 const int MAPINFO_FEATURE_VEHICLES      = 2;
 const int MAPINFO_FEATURE_TURRETS       = 4;
index 71c731a308e830ac59d3d3b323b177edc4444e8e..2bcdd573cdb5f17301438ccd8fe21e802073e4f1 100644 (file)
@@ -279,6 +279,7 @@ void cvar_changes_init()
                BADCVAR("g_race_laps_limit");
                BADCVAR("g_race_qualifying_timelimit");
                BADCVAR("g_race_qualifying_timelimit_override");
+               BADCVAR("g_singleplayer");
                BADCVAR("g_snafu");
                BADCVAR("g_tdm");
                BADCVAR("g_tdm_teams");
index 6835f5d560b9a325a96e671c21ec17348915ff2a..92c180d446703f33a2a2fd73918d832874aaf77c 100644 (file)
@@ -11,4 +11,5 @@
 #include <server/mutators/mutator/gamemode_keyhunt.qc>
 #include <server/mutators/mutator/gamemode_lms.qc>
 #include <server/mutators/mutator/gamemode_race.qc>
+#include <server/mutators/mutator/gamemode_singleplayer.qc>
 #include <server/mutators/mutator/gamemode_tdm.qc>
index aef0b332abbaa9a3c5c27560d3e0bcca21297005..b3ca9c6985c343221ee4df7610c27313dc75e451 100644 (file)
@@ -11,4 +11,5 @@
 #include <server/mutators/mutator/gamemode_keyhunt.qh>
 #include <server/mutators/mutator/gamemode_lms.qh>
 #include <server/mutators/mutator/gamemode_race.qh>
+#include <server/mutators/mutator/gamemode_singleplayer.qh>
 #include <server/mutators/mutator/gamemode_tdm.qh>
diff --git a/qcsrc/server/mutators/mutator/gamemode_singleplayer.qc b/qcsrc/server/mutators/mutator/gamemode_singleplayer.qc
new file mode 100644 (file)
index 0000000..e2dd8ab
--- /dev/null
@@ -0,0 +1,110 @@
+#include "gamemode_singleplayer.qh"
+
+/*QUAKED spawnfunc_tdm_team (0 .5 .8) (-16 -16 -24) (16 16 32)
+Team declaration for TDM gameplay, this allows you to decide what team names and control point models are used in your map.
+Note: If you use spawnfunc_tdm_team entities you must define at least 2!  However, unlike domination, you don't need to make a blank one too.
+Keys:
+"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)...
+"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */
+spawnfunc(sp_team)
+{
+    if(!g_singleplayer || !this.cnt) { delete(this); return; }
+
+    this.classname = "sp_team";
+    this.team = this.cnt + 1;
+}
+
+// code from here on is just to support maps that don't have team entities
+void sp_SpawnTeam (string teamname, int teamcolor)
+{
+    entity this = new_pure(sp_team);
+    this.netname = teamname;
+    this.cnt = teamcolor - 1;
+    this.team = teamcolor;
+    this.spawnfunc_checked = true;
+    //spawnfunc_sp_team(this);
+}
+
+
+// spawnfuncs
+spawnfunc(info_player_singleplayer)
+{
+    if (!g_singleplayer) { delete(this); return; }
+
+    this.team = NUM_TEAM_1;
+    spawnfunc_info_player_deathmatch(this);
+}
+
+spawnfunc(info_player_singleplayer_enemy)
+{
+       if (!g_singleplayer) { delete(this); return; }
+
+       this.team = NUM_TEAM_2;
+       spawnfunc_info_player_deathmatch(this);
+}
+
+void sp_DelayedInit(entity this)
+{
+    // if no teams are found, spawn defaults
+    if(find(NULL, classname, "sp_team") == NULL)
+    {
+        LOG_TRACE("No \"sp_team\" entities found on this map, creating them anyway.");
+
+        sp_SpawnTeam("Player", NUM_TEAM_1);
+        sp_SpawnTeam("Enemy", NUM_TEAM_2);
+    }
+}
+
+MUTATOR_HOOKFUNCTION(sp, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE)
+{
+    LOG_TRACEF("\n==== here ====\n");
+       M_ARGV(1, string) = "sp_team";
+    entity ent = M_ARGV(2, entity);
+    if ( IS_BOT_CLIENT(ent) )
+    {
+        ent.team_forced = NUM_TEAM_2;
+    }
+    else if( IS_PLAYER(ent) )
+    {
+        ent.team_forced = NUM_TEAM_1;
+    }
+    LOG_TRACEF("\n==== After processing ====\nent: %s\nteam: %d\n\n",
+               etos(ent), ent.team_forced);
+    return true;
+}
+
+MUTATOR_HOOKFUNCTION(sp, Scores_CountFragsRemaining)
+{
+    return false;
+}
+
+
+.bool can_drop_weapon;
+.string weapon_name;
+
+MUTATOR_HOOKFUNCTION(sp, PlayerSpawn)
+{
+       entity player = M_ARGV(0, entity);
+       entity spawn_spot = M_ARGV(1, entity);
+    if ( IS_BOT_CLIENT(player) )
+    {
+        player.can_drop_weapon = spawn_spot.can_drop_weapon;
+        player.items |= IT_UNLIMITED_WEAPON_AMMO;
+        if ( spawn_spot.health )
+            player.health = spawn_spot.health;
+        player.armorvalue = spawn_spot.armorvalue;
+        player.weapons = WepSet_FromWeapon(Weapons_fromstr(spawn_spot.weapon_name));
+        if ( spawn_spot.netname )
+            player.netname = spawn_spot.netname;
+    }
+    else
+    {
+        player.can_drop_weapon = true;
+    }
+}
+
+MUTATOR_HOOKFUNCTION(sp, ForbidDropCurrentWeapon)
+{
+    entity player = M_ARGV(0, entity);
+    return !player.can_drop_weapon;
+}
diff --git a/qcsrc/server/mutators/mutator/gamemode_singleplayer.qh b/qcsrc/server/mutators/mutator/gamemode_singleplayer.qh
new file mode 100644 (file)
index 0000000..039aceb
--- /dev/null
@@ -0,0 +1,35 @@
+#pragma once
+
+#include "../gamemode.qh"
+
+void sp_DelayedInit(entity this);
+
+REGISTER_MUTATOR(sp, false)
+{
+       MUTATOR_ONADD
+       {
+               if (time > 1) // game loads at time 1
+                       error("This is a game type and it cannot be added at runtime.");
+               InitializeEntity(NULL, sp_DelayedInit, INITPRIO_GAMETYPE);
+
+               ActivateTeamplay();
+               SetLimits(-1, -1, autocvar_timelimit_override, -1);
+               have_team_spawns = -1; // request team spawns
+       }
+
+       MUTATOR_ONROLLBACK_OR_REMOVE
+       {
+               // we actually cannot roll back tdm_Initialize here
+               // BUT: we don't need to! If this gets called, adding always
+               // succeeds.
+       }
+
+       MUTATOR_ONREMOVE
+       {
+               LOG_INFO("This is a game type and it cannot be removed at runtime.");
+               return -1;
+       }
+
+       return 0;
+}
+