X-Git-Url: http://de.git.xonotic.org/?a=blobdiff_plain;f=qcsrc%2Fserver%2Fmutators%2Fsandbox.qc;h=7de8f4cb053cbee05b02deef4eb03984d5d20db3;hb=39589979989c5c661d1bfc61e1210a1efcd44ac1;hp=ddcdf0a751abd5aab0da939050f841d7b74a5453;hpb=8a6096e7a93494a4ca747d6bcf88f53da2552b01;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/mutators/sandbox.qc b/qcsrc/server/mutators/sandbox.qc index ddcdf0a75..7de8f4cb0 100644 --- a/qcsrc/server/mutators/sandbox.qc +++ b/qcsrc/server/mutators/sandbox.qc @@ -1,10 +1,11 @@ +const float MAX_STORAGE_ATTACHMENTS = 16; float object_count; .string object_clipboard; .entity object_attach; .string material; .float touch_timer; -void sandbox_Object_Touch() +void sandbox_ObjectFunction_Touch() { // apply material impact effects @@ -29,9 +30,10 @@ void sandbox_Object_Touch() pointparticles(particleeffectnum(strcat("impact_", self.material)), self.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 } -entity sandbox_ObjectEdit_Get() +entity sandbox_ObjectEdit_Get(float permissions) { // returns the traced entity if the player can edit it, and world if not + // if permissions if FALSE, the object is returned regardless of editing rights // attached objects are SOLID_NOT and don't risk getting traced makevectors(self.v_angle); @@ -39,9 +41,11 @@ entity sandbox_ObjectEdit_Get() if(trace_ent.classname != "object") return world; // entity is not an object + if(!permissions) + return trace_ent; // don't check permissions, anyone can edit this object if(!trace_ent.crypto_idfp) return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it - else if not(trace_ent.crypto_idfp != self.crypto_idfp && !autocvar_g_sandbox_editor_free) + if not(trace_ent.crypto_idfp != self.crypto_idfp && autocvar_g_sandbox_editor_free < 2) return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server return world; } @@ -56,12 +60,13 @@ void sandbox_ObjectEdit_Scale(entity e, float f) } } -.float old_movetype; +void sandbox_ObjectAttach_Remove(entity e); void sandbox_ObjectAttach_Set(entity e, entity parent, string s) { // attaches e to parent on string s - e.old_movetype = e.movetype; // persist this + // we can't attach to an attachment, for obvious reasons + sandbox_ObjectAttach_Remove(e); e.movetype = MOVETYPE_FOLLOW; e.solid = SOLID_NOT; @@ -80,7 +85,7 @@ void sandbox_ObjectAttach_Remove(entity e) { if(head.owner == e) { - head.movetype = head.old_movetype; // revert to previous physics + head.movetype = MOVETYPE_TOSS; // default head.solid = SOLID_BBOX; head.takedamage = DAMAGE_AIM; @@ -108,7 +113,7 @@ entity sandbox_ObjectSpawn(float database) e.frame = 0; e.skin = 0; e.material = string_null; - e.touch = sandbox_Object_Touch; + e.touch = sandbox_ObjectFunction_Touch; if(!database) { @@ -149,104 +154,169 @@ void sandbox_ObjectRemove(entity e) object_count -= 1; } +string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global + string sandbox_ObjectPort_Save(entity e, float database) { - // save object properties + // TODO: Store attachement bone for attached objects as well! + + // save object properties, and return them as a string + float i; string s; + entity head; - s = strcat(e.model, " "); - s = strcat(s, ftos(e.skin), " "); - s = strcat(s, ftos(e.alpha), " "); - s = strcat(s, sprintf("\"%.9v\"", e.colormod), " "); - s = strcat(s, sprintf("\"%.9v\"", e.glowmod), " "); - s = strcat(s, ftos(e.frame), " "); - s = strcat(s, ftos(e.scale), " "); - s = strcat(s, ftos(e.movetype), " "); - s = strcat(s, ftos(e.damageforcescale), " "); - if(e.material) s = strcat(s, e.material, " "); else s = strcat(s, "- "); // none - if(database) + for(head = world; (head = find(head, classname, "object")); ) { - if(e.crypto_idfp) s = strcat(s, e.crypto_idfp, " "); else s = strcat(s, "- "); // none - s = strcat(s, sprintf("\"%.9v\"", e.origin), " "); - s = strcat(s, sprintf("\"%.9v\"", e.angles), " "); + // the main object needs to be first in the array [0], with attached objects following + float slot; + if(head == e) // this is the main object, make it first + slot = 0; + else if(head.owner == e) // child object, list them in order + { + i += 1; // children start from 1 + slot = i; + } + else + continue; + + port_string[slot] = strcat(port_string[slot], head.model, " "); + port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.movetype), " "); + port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); + if(head.material) port_string[slot] = strcat(port_string[slot], head.material, " "); else port_string[slot] = strcat(port_string[slot], "- "); // none + if(database) + { + if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], head.crypto_idfp, " "); else port_string[slot] = strcat(port_string[slot], "- "); // none + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); + } + } + + // now apply the array to a simple string, with the ; symbol separating objects + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + { + if(port_string[i]) + s = strcat(s, port_string[i], "; "); + port_string[i] = string_null; // fully clear the string } return s; } -void sandbox_ObjectPort_Load(entity e, string s, float database) +entity sandbox_ObjectPort_Load(string s, float database) { - // load object properties - tokenize_console(s); - - setmodel(e, argv(0)); - e.skin = stof(argv(1)); - e.alpha = stof(argv(2)); - e.colormod = stov(argv(3)); - e.glowmod = stov(argv(4)); - e.frame = stof(argv(5)); - sandbox_ObjectEdit_Scale(e, stof(argv(6))); - e.movetype = stof(argv(7)); - e.damageforcescale = stof(argv(8)); - if(e.material) strunzone(e.material); if(argv(9) != "-") e.material = strzone(argv(9)); else e.material = string_null; - if(database) + // TODO: Store attachement bone for attached objects as well! + + // load object properties, and spawn a new object with them + float n, i; + entity e, parent; + + // separate objects between the ; symbols + n = tokenizebyseparator(s, "; "); + for(i = 0; i < n; ++i) + port_string[i] = argv(i); + + // now separate and apply the properties of each object + for(i = 0; i < n; ++i) { - if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(10) != "-") e.crypto_idfp = strzone(argv(10)); else e.crypto_idfp = string_null; - setorigin(e, stov(argv(11))); - e.angles = stov(argv(12)); + tokenize_console(port_string[i]); + e = sandbox_ObjectSpawn(database); + + setmodel(e, argv(0)); + e.skin = stof(argv(1)); + e.alpha = stof(argv(2)); + e.colormod = stov(argv(3)); + e.glowmod = stov(argv(4)); + e.frame = stof(argv(5)); + sandbox_ObjectEdit_Scale(e, stof(argv(6))); + e.movetype = stof(argv(7)); + e.damageforcescale = stof(argv(8)); + if(e.material) strunzone(e.material); if(argv(9) != "-") e.material = strzone(argv(9)); else e.material = string_null; + if(database) + { + if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(10) != "-") e.crypto_idfp = strzone(argv(10)); else e.crypto_idfp = string_null; + setorigin(e, stov(argv(11))); + e.angles = stov(argv(12)); + } + + if(!i) // parent object, set it as such and leave it be + parent = e; + else // child object, attach it to the parent + sandbox_ObjectAttach_Set(e, parent, ""); } + + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + port_string[i] = string_null; // fully clear the string + + return e; } -void sandbox_DatabaseSave() +void sandbox_Database_Save() { // saves all objects to the database file - if(!object_count) - return; // nothing to store - entity head; - float get_file; + string file_name; + float file_get; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_WRITE); + fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(TRUE, "%d-%m-%Y %H:%M:%S"))); + fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); - get_file = fopen(strcat("sandbox/storage_", GetMapname(), ".txt"), FILE_WRITE); - fputs(get_file, strcat("// sandbox storage for map ", GetMapname(), ", containing a total of ", ftos(object_count), " objects\n")); for(head = world; (head = find(head, classname, "object")); ) { - // Unfortunately, attached objects cannot be persisted yet. That's because the parent is specified as an entity, - // but only properties can be saved to this file, leaving no way to identify the owner once all objects are spawned - // TODO: Find some way to fix this! + // attached objects are persisted separately, ignore them here if(head.owner != world) continue; - // use a line for each object, listing all properties - fputs(get_file, strcat(sandbox_ObjectPort_Save(head, TRUE), "\n")); + // use a line of text for each object, listing all properties + fputs(file_get, strcat(sandbox_ObjectPort_Save(head, TRUE), "\n")); } - fclose(get_file); + fclose(file_get); } -void sandbox_DatabaseLoad() +void sandbox_Database_Load() { // loads all objects from the database file - string file; - float get_file; + string file_read, file_name; + float file_get, i; - get_file = fopen(strcat("sandbox/storage_", GetMapname(), ".txt"), FILE_READ); - if(get_file < 0) - { } // message comes here + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_READ); + if(file_get < 0) + { + if(autocvar_g_sandbox_info > 0) + print(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); + } else { for(;;) { - file = fgets(get_file); - if(!file) + file_read = fgets(file_get); + if(!file_read) break; - if(substring(file, 0, 2) == "//") + if(substring(file_read, 0, 2) == "//") continue; - if(substring(file, 0, 1) == "#") + if(substring(file_read, 0, 1) == "#") continue; entity e; - e = sandbox_ObjectSpawn(TRUE); - sandbox_ObjectPort_Load(e, file, TRUE); + e = sandbox_ObjectPort_Load(file_read, TRUE); + + if(e.material) + { + // since objects are being loaded for the first time, precache material sounds for each + for (i = 1; i <= 5; i++) // 5 sounds in total + precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".ogg")); + } } + if(autocvar_g_sandbox_info > 0) + print(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); } } @@ -273,7 +343,6 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print_to(self, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); print_to(self, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); print_to(self, "^7\"^2object_duplicate ^3value^7\" duplicates the object. 'copy' copies the object, 'paste' puts it in front of the player"); - print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); print_to(self, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); print_to(self, "^7Attachment properties for ^2object_attach^7:"); print_to(self, "^3get ^7- selects the object you are facing as the object to be attached"); @@ -290,10 +359,12 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print_to(self, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); print_to(self, "^3force value ^7- amount of force applied to objects that are shot"); print_to(self, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); + print_to(self, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); + print_to(self, "^7\"^2object_info ^3value^7\" shows public information about the object. 'mesh' shows model information"); print_to(self, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); return TRUE; - // ---------------- COMMAND: SPAWN OBJECT ---------------- + // ---------------- COMMAND: OBJECT, SPAWN ---------------- case "object_spawn": if(object_count >= autocvar_g_sandbox_editor_maxobjects) { @@ -318,9 +389,9 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print(strcat("^3SANDBOX - SERVER: ^7", self.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); return TRUE; - // ---------------- COMMAND: REMOVE OBJECT ---------------- + // ---------------- COMMAND: OBJECT, REMOVE ---------------- case "object_remove": - e = sandbox_ObjectEdit_Get(); + e = sandbox_ObjectEdit_Get(TRUE); if(e != world) { if(autocvar_g_sandbox_info > 0) @@ -332,13 +403,13 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print_to(self, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); return TRUE; - // ---------------- COMMAND: DUPLICATE OBJECT ---------------- + // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- case "object_duplicate": switch(argv(2)) { case "copy": // copies customizable properties of the selected object to the clipboard - e = sandbox_ObjectEdit_Get(); // you can only copy objects you can edit, so this works + e = sandbox_ObjectEdit_Get(autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? if(e != world) { if(self.object_clipboard) @@ -348,7 +419,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print_to(self, "^2SANDBOX - INFO: ^7Object copied to clipboard"); return TRUE; } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have edit rights over"); + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); return TRUE; case "paste": @@ -364,8 +435,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) return TRUE; } - e = sandbox_ObjectSpawn(FALSE); - sandbox_ObjectPort_Load(e, self.object_clipboard, FALSE); + e = sandbox_ObjectPort_Load(self.object_clipboard, FALSE); print_to(self, "^2SANDBOX - INFO: ^7Object pasted successfully"); if(autocvar_g_sandbox_info > 0) @@ -374,38 +444,13 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) } return TRUE; - // ---------------- COMMAND: CLAIM OBJECT ---------------- - case "object_claim": - // if the player can edit an object but is not its owner, this can be used to claim that object - if(self.crypto_idfp == "") - { - print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); - return TRUE; - } - e = sandbox_ObjectEdit_Get(); - if(e != world) - { - if(e.crypto_idfp == self.crypto_idfp) - { - print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); - return TRUE; - } - - if(e.crypto_idfp) - strunzone(e.crypto_idfp); - e.crypto_idfp = self.crypto_idfp; - print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully"); - } - print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); - return TRUE; - - // ---------------- COMMAND: ATTACH OBJECT ---------------- + // ---------------- COMMAND: OBJECT, ATTACH ---------------- case "object_attach": switch(argv(2)) { case "get": // select e as the object as meant to be attached - e = sandbox_ObjectEdit_Get(); + e = sandbox_ObjectEdit_Get(TRUE); if(e != world) { self.object_attach = e; @@ -422,7 +467,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) } // attaches the previously selected object to e - e = sandbox_ObjectEdit_Get(); + e = sandbox_ObjectEdit_Get(TRUE); if(e != world) { sandbox_ObjectAttach_Set(self.object_attach, e, argv(3)); @@ -436,7 +481,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) return TRUE; case "remove": // removes e if it was attached - e = sandbox_ObjectEdit_Get(); + e = sandbox_ObjectEdit_Get(TRUE); if(e != world) { sandbox_ObjectAttach_Remove(e); @@ -450,7 +495,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) } return TRUE; - // ---------------- COMMAND: EDIT OBJECT ---------------- + // ---------------- COMMAND: OBJECT, EDIT ---------------- case "object_edit": if(!argv(2)) { @@ -458,7 +503,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) return TRUE; } - e = sandbox_ObjectEdit_Get(); + e = sandbox_ObjectEdit_Get(TRUE); if(e != world) { switch(argv(2)) @@ -524,6 +569,50 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerCommand) print_to(self, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); return TRUE; + // ---------------- COMMAND: OBJECT, CLAIM ---------------- + case "object_claim": + // if the player can edit an object but is not its owner, this can be used to claim that object + if(self.crypto_idfp == "") + { + print_to(self, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); + return TRUE; + } + e = sandbox_ObjectEdit_Get(TRUE); + if(e != world) + { + if(e.crypto_idfp == self.crypto_idfp) + { + print_to(self, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); + return TRUE; + } + + if(e.crypto_idfp) + strunzone(e.crypto_idfp); + e.crypto_idfp = self.crypto_idfp; + print_to(self, "^2SANDBOX - INFO: ^7Object claimed successfully"); + } + print_to(self, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); + return TRUE; + + // ---------------- COMMAND: OBJECT, INFO ---------------- + case "object_info": + // prints public information about the object to the player + e = sandbox_ObjectEdit_Get(FALSE); + if(e != world) + { + switch(argv(2)) + { + case "mesh": + string tags; + for(i = 1; gettaginfo(e, i); i++) + tags = strcat(tags, "^5", gettaginfo_name, "^7, "); + print_to(self, strcat("^2SANDBOX - INFO: ^7Object mesh is ^3", e.model, "^7 at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", tags)); + return TRUE; + } + } + print_to(self, "^1SANDBOX - WARNING: ^7No information could not be found. Make sure you are facing an object"); + return TRUE; + // ---------------- COMMAND: DEFAULT ---------------- default: print_to(self, "Invalid command. For usage information, type 'sandbox help'"); @@ -548,7 +637,7 @@ MUTATOR_HOOKFUNCTION(sandbox_PlayerPreThink) entity e; float grab; - e = sandbox_ObjectEdit_Get(); + e = sandbox_ObjectEdit_Get(TRUE); if(e != world && vlen(e.origin - self.origin) <= autocvar_g_sandbox_editor_distance_edit) grab = TRUE; @@ -575,11 +664,11 @@ MUTATOR_HOOKFUNCTION(sandbox_StartFrame) { if(!autocvar_g_sandbox_storage_autosave) return FALSE; - if(time < autocvar_g_sandbox_storage_autosave) + if(time < autosave_time) return FALSE; autosave_time = time + autocvar_g_sandbox_storage_autosave; - sandbox_DatabaseSave(); + sandbox_Database_Save(); return TRUE; } @@ -595,7 +684,7 @@ MUTATOR_DEFINITION(sandbox) { autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame if(autocvar_g_sandbox_storage_autoload) - sandbox_DatabaseLoad(); + sandbox_Database_Load(); } return FALSE;