alias selectteam "qc_cmd_cmd selectteam ${* ?}" // Attempt to choose a team to join into
alias selfstuff "qc_cmd_cmd selfstuff ${* ?}" // Stuffcmd a command to your own client
alias sentcvar "qc_cmd_cmd sentcvar ${* ?}" // New system for sending a client cvar to the server
+alias editmob "qc_cmd_cmd mobedit ${* ?}" // Edit a monster's properties
+alias killmob "qc_cmd_cmd mobkill ${* ?}" // Kill a monster
alias spawnmob "qc_cmd_cmd mobspawn ${* ?}" // Spawn a monster infront of the player
alias spectate "qc_cmd_cmd spectate ${* ?}" // Become an observer
alias suggestmap "qc_cmd_cmd suggestmap ${* ?}" // Suggest a map to the mapvote at match end
bind f7 menu_showsandboxtools
+seta menu_monsters_spawn_monster ""
+seta menu_monsters_edit_name ""
+seta menu_monsters_edit_skin 0
+seta menu_monsters_edit_color ""
+seta menu_monsters_edit_movetarget 1
+
set g_playerclip_collisions 1 "0 = disable collision testing against playerclips, might be useful on some defrag maps"
set g_botclip_collisions 1 "0 = disable collision testing against botclips, might be useful on some defrag maps"
#include "xonotic/dialog_firstrun.c"
#include "xonotic/dialog_teamselect.c"
#include "xonotic/dialog_sandboxtools.c"
+#include "xonotic/dialog_monstertools.c"
#include "xonotic/dialog_settings.c"
#include "xonotic/dialog_settings_video.c"
#include "xonotic/dialog_settings_effects.c"
--- /dev/null
+#ifdef INTERFACE
+CLASS(XonoticMonsterToolsDialog) EXTENDS(XonoticRootDialog)
+ METHOD(XonoticMonsterToolsDialog, fill, void(entity)) // to be overridden by user to fill the dialog with controls
+ ATTRIB(XonoticMonsterToolsDialog, title, string, _("Monster Tools"))
+ ATTRIB(XonoticMonsterToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS)
+ ATTRIB(XonoticMonsterToolsDialog, intendedWidth, float, 0.8)
+ ATTRIB(XonoticMonsterToolsDialog, rows, float, 16)
+ ATTRIB(XonoticMonsterToolsDialog, columns, float, 4)
+ ATTRIB(XonoticMonsterToolsDialog, name, string, "MonsterTools")
+ENDCLASS(XonoticMonsterToolsDialog)
+#endif
+
+#ifdef IMPLEMENTATION
+void XonoticMonsterToolsDialog_fill(entity me)
+{
+ entity e, box;
+
+ me.TR(me);
+ me.TD(me, 1, 0.25, e = makeXonoticTextLabel(0, _("Monster:")));
+ me.TD(me, 1, 1.5, box = makeXonoticInputBox(1, "menu_monsters_spawn_monster"));
+ box.forbiddenCharacters = "\r\n\\\"$"; // don't care, isn't getting saved
+ box.maxLength = -127; // negative means encoded length in bytes
+ box.saveImmediately = 1;
+ me.TD(me, 1, 0.25, e = makeXonoticTextLabel(0, _("Name:")));
+ me.TD(me, 1, 1.5, box = makeXonoticInputBox(1, "menu_monsters_edit_name"));
+ box.forbiddenCharacters = "\r\n\\\"$"; // don't care, isn't getting saved
+ box.maxLength = -127; // negative means encoded length in bytes
+ box.saveImmediately = 1;
+ me.TR(me);
+ me.TDempty(me, 0.5);
+ me.TD(me, 1, 0.5, e = makeXonoticCommandButton(_("Spawn"), '0 0 0', "cmd mobspawn $menu_monsters_spawn_monster $menu_monsters_edit_movetarget \"$menu_monsters_edit_name\"", 0));
+ me.TD(me, 1, 0.5, e = makeXonoticCommandButton(_("Remove"), '0 0 0', "cmd mobkill", 0));
+ me.TD(me, 1, 0.5, e = makeXonoticCommandButton(_("Change name"), '0 0 0', "editmob name \"$menu_monsters_edit_name\"", 0));
+ me.TR(me);
+ me.TD(me, 1, 0.5, e = makeXonoticCommandButton(_("Move target:"), '0 0 0', "editmob movetarget $menu_monsters_edit_movetarget", 0));
+ me.TD(me, 1, 0.5, e = makeXonoticRadioButton(2, "menu_monsters_edit_movetarget", "1", _("Follow")));
+ me.TD(me, 1, 0.5, e = makeXonoticRadioButton(2, "menu_monsters_edit_movetarget", "2", _("Wander around")));
+ me.TD(me, 1, 0.5, e = makeXonoticRadioButton(2, "menu_monsters_edit_movetarget", "3", _("Spawnpoint")));
+ me.TD(me, 1, 0.5, e = makeXonoticRadioButton(2, "menu_monsters_edit_movetarget", "4", _("No moving")));
+ me.TR(me);
+ me.TD(me, 1, 1.5, e = makeXonoticTextLabel(0, _("Colors:")));
+ me.TR(me);
+ me.TD(me, 1, 0.5, e = makeXonoticCommandButton(_("Set skin:"), '0 0 0', "editmob skin $menu_monsters_edit_skin", 0));
+ me.TD(me, 1, 1.5, e = makeXonoticSlider(0, 99, 1, "menu_monsters_edit_skin"));
+ me.TR(me);
+ me.TD(me, 1, 0.5, e = makeXonoticCommandButton(_("Set color:"), '0 0 0', "editmob color \"$menu_monsters_edit_color\"", 0));
+ me.TD(me, 2, 1.5, e = makeXonoticColorpickerString("menu_monsters_edit_color", "menu_monsters_edit_color"));
+ me.TR(me);
+
+ me.gotoRC(me, me.rows - 1, 0);
+ me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0'));
+ e.onClick = Dialog_Close;
+ e.onClickEntity = me;
+}
+#endif
+
+/* Click. The c-word is here so you can grep for it :-) */
i.configureDialog(i);
me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z);
+ i = spawnXonoticMonsterToolsDialog();
+ i.configureDialog(i);
+ me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS);
+
// main dialogs/windows
me.mainNexposee = n = spawnXonoticNexposee();
}
}
+void ClientCommand_mobedit(float request, float argc)
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ makevectors(self.v_angle);
+ WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self);
+
+ if not(trace_ent.flags & FL_MONSTER) { sprint(self, "You need to aim at your monster to edit its properties.\n"); return; }
+ if(trace_ent.realowner != self) { sprint(self, "That monster does not belong to you.\n"); return; }
+
+ switch(argv(1))
+ {
+ case "name": trace_ent.netname = strzone(strdecolorize(argv(2))); WaypointSprite_UpdateSprites(trace_ent.sprite, trace_ent.netname, "", ""); break;
+ case "skin": trace_ent.skin = stof(argv(2)); break;
+ case "color": trace_ent.colormod = stov(argv(2)); break;
+ case "movetarget": trace_ent.monster_moveflags = stof(argv(2)); break;
+ default: sprint(self, "Unknown parameter\n"); break;
+ }
+
+ return; // never fall through to usage
+ }
+ default:
+ sprint(self, "Incorrect parameters for ^2mobedit^7\n");
+ case CMD_REQUEST_USAGE:
+ {
+ sprint(self, "\nUsage:^3 cmd mobedit [argument]\n");
+ sprint(self, " Where 'argument' can be name, skin, color or movetarget.\n");
+ return;
+ }
+ }
+}
+
+void ClientCommand_mobkill(float request)
+{
+ switch(request)
+ {
+ case CMD_REQUEST_COMMAND:
+ {
+ makevectors(self.v_angle);
+ WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 100, MOVE_NORMAL, self);
+
+ if(trace_ent.flags & FL_MONSTER)
+ {
+ if(trace_ent.realowner != self)
+ {
+ sprint(self, "That monster does not belong to you.\n");
+ return;
+ }
+ sprint(self, strcat("Your pet '", trace_ent.netname, "' has been brutally mutilated.\n"));
+ Damage (trace_ent, world, world, trace_ent.health + trace_ent.max_health, DEATH_KILL, trace_ent.origin, '0 0 0');
+ return;
+ }
+ else
+ {
+ sprint(self, "You need to aim at your monster to kill it.\n");
+ }
+
+ return;
+ }
+
+ default:
+ sprint(self, "Incorrect parameters for ^2mobkill^7\n");
+ case CMD_REQUEST_USAGE:
+ {
+ sprint(self, "\nUsage:^3 cmd mobkill\n");
+ sprint(self, " Aim at your monster to kill it.\n");
+ return;
+ }
+ }
+}
+
void ClientCommand_mobspawn(float request, float argc)
{
switch(request)
CLIENT_COMMAND("clientversion", ClientCommand_clientversion(request, arguments), "Release version of the game") \
CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(request, arguments), "Retrieve mapshot picture from the server") \
CLIENT_COMMAND("join", ClientCommand_join(request), "Become a player in the game") \
+ CLIENT_COMMAND("mobedit", ClientCommand_mobedit(request, arguments), "Edit your monster's properties") \
+ CLIENT_COMMAND("mobkill", ClientCommand_mobkill(request), "Kills your monster") \
CLIENT_COMMAND("mobspawn", ClientCommand_mobspawn(request, arguments), "Spawn monsters infront of yourself") \
CLIENT_COMMAND("ready", ClientCommand_ready(request), "Qualify as ready to end warmup stage (or restart server if allowed)") \
CLIENT_COMMAND("say", ClientCommand_say(request, arguments, command), "Print a message to chat to all players") \
float monsters_total;
float monsters_killed;
void monsters_setstatus(); // monsters.qc
+.float monster_moveflags; // checks where to move when not attacking
.void() monster_die;
.void() monster_delayedattack;
-.float monster_moveflags; // checks where to move when not attacking
.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