X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;ds=inline;f=qcsrc%2Fserver%2Fattic%2Frunematch.qc;fp=qcsrc%2Fserver%2Fattic%2Frunematch.qc;h=ba8f648c888b33e0e312cdf8774b3e1cd0169cad;hb=4779c1bd8269aaca0d20e802c7e8e685602bb926;hp=0000000000000000000000000000000000000000;hpb=39c12453fec6998ed3e660ac5279845d07fb525c;p=xonotic%2Fxonotic-data.pk3dir.git diff --git a/qcsrc/server/attic/runematch.qc b/qcsrc/server/attic/runematch.qc new file mode 100644 index 000000000..ba8f648c8 --- /dev/null +++ b/qcsrc/server/attic/runematch.qc @@ -0,0 +1,604 @@ +float rune_numspawns; + +float RUNE_FIRST = 1; +float RUNE_STRENGTH = 1; +float RUNE_DEFENSE = 2; +float RUNE_REGEN = 4; +float RUNE_SPEED = 8; +float RUNE_VAMPIRE = 16; +float RUNE_LAST = 16; + +float CURSE_FIRST = 8192; +float CURSE_WEAK = 8192; +float CURSE_VULNER = 16384; +float CURSE_VENOM = 32768; +float CURSE_SLOW = 65536; +float CURSE_EMPATHY = 131072; +float CURSE_LAST = 131072; + +float RUNE_COUNT = 5; + +/* rune ideas: + + Doom/Death + Rune: When you damage enemies, you have a slight chance of instant-killing them (porportional to damage dealt / their health) + Curse: When you are damaged, you have a chance of being instant-killed + + Vengence/Slothful + Rune: The lower your health below 100, the more damage you deal (does not decrease your damage if you're above 100) + Curse: The higher your health (up to 100), the less damage you deal (at 100 hp deal 1/5th damage) + +*/ + +/*QUAKED spawnfunc_runematch_spawn_point (1 0 0) (-16 -16 -24) (16 16 24) +spawn point for runes in runematch +*/ + +void spawnfunc_runematch_spawn_point() +{ + if(!g_runematch || !autocvar_g_runematch_fixedspawns) + { + remove(self); + return; + } + + setsize(self, '0 0 -35', '0 0 0'); + droptofloor(); + ++rune_numspawns; +} + +// only used if using rune spawns at all +entity rune_find_spawnpoint() +{ + entity e; + + if(rune_numspawns < RUNE_COUNT) + return world; + + RandomSelection_Init(); + + for(e = world; (e = find(e, classname, "runematch_spawn_point")); ) + if(e.owner == world) + RandomSelection_Add(e, 0, string_null, e.cnt, 0); + + return RandomSelection_chosen_ent; +} + +float rune_spawn_somewhere(entity e) +{ + entity spot; + spot = rune_find_spawnpoint(); + if(spot) + { + spot.owner = e; + setorigin(e, spot.origin); + + e.owner = spot; + spot.owner = e; + + return TRUE; + } + else + { + if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) + { + // great + makevectors(self.angles), + self.velocity = v_forward * 250; + self.angles = '0 0 0'; + return TRUE; + } + else + { + // sorry, can't spawn, better luck next frame + return FALSE; + } + } +} + +void rune_unmark_spot(entity e) +{ + if(e.owner.classname == "runematch_spawn_point") + { + e.owner.owner = world; + e.owner = world; + } +} + +string RuneName(float r) +{ + if(r == RUNE_STRENGTH) + return "^1Strength^7"; + if(r == RUNE_DEFENSE) + return "^4Defense^7"; + if(r == RUNE_REGEN) + return "^2Vitality^7"; + if(r == RUNE_SPEED) + return "^3Speed^7"; + if(r == RUNE_VAMPIRE) + return "^6Vampire^7"; + + if(r == CURSE_WEAK) + return "^1Weakness^7"; + if(r == CURSE_VULNER) + return "^4Vulnerability^7"; + if(r == CURSE_VENOM) + return "^2Venom^7"; + if(r == CURSE_SLOW) + return "^3Slow^7"; + if(r == CURSE_EMPATHY) + return "^6Empathy^7"; + return strcat("^8[unnamed", ftos(r), "]^7"); +} + +vector RuneColormod(float r) +{ + vector _color = '255 0 255'; + + if(r == RUNE_STRENGTH) + _color = '255 0 0'; + if(r == RUNE_DEFENSE) + _color = '0 0 255';//'0 102 255';// + if(r == RUNE_REGEN) + _color = '0 204 0';//'0 255 0'; + if(r == RUNE_SPEED) + _color = 0.35*'185 185 0';//255 230 0';//'255 255 0'; + if(r == RUNE_VAMPIRE) + _color = '64 0 128';//'108 0 217';//'128 0 255';//'179 0 204';// + + if(r == CURSE_WEAK) + _color = '255 0 0'; + if(r == CURSE_VULNER) + _color = '0 0 255';//'0 102 255';// + if(r == CURSE_VENOM) + _color = '0 204 0';//'0 255 0'; + if(r == CURSE_SLOW) + _color = 0.5*'185 185 0';//'255 255 0'; + if(r == CURSE_EMPATHY) + _color = '179 0 204';//'128 0 255'; + + return _color * (1 / 255) * autocvar_g_runematch_rune_color_strength; +} + +void rune_respawn(); + +void RuneCarriedThink() +{ + float rcount, rnum; + vector ang = '0 0 0'; + entity rune; + + if(self.owner.classname != "player" || time < game_starttime) + { + rune_respawn(); + return; + } + + self.nextthink = time + 0.1; + + // count runes my owner holds + rcount = 0; + rune = find(world, classname, "rune"); + rnum = -1; + while(rune) + { + if(rune.owner == self.owner) + rcount = rcount + 1; + if(rune == self) + rnum = rcount; + rune = find(rune, classname, "rune"); + } + + ang_y = rnum*(360 / rcount) + mod(time, 360)*45;//180; + + makevectors(ang); + + setorigin(self, v_forward*32); +} + +void rune_touch() +{ + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { + self.think = rune_respawn; + self.nextthink = time; + return; + } + + if(other.classname != "player" || other.health < 1) + return; + if(self.wait > time) + return; // "notouch" time isn't finished + + // detach from the spawn point you're on + rune_unmark_spot(self); + + self.owner = other; + self.enemy.owner = other; + setattachment(self, other, ""); + + other.runes = other.runes | self.runes | self.enemy.runes; + + //self.think = func_null; + //self.nextthink = 0; + self.think = RuneCarriedThink; + self.nextthink = time; + self.touch = func_null; + + self.solid = SOLID_NOT; + setorigin(self, self.origin); + + //sprint(other, strcat("^3You have picked up ", + // RuneName(self.runes & (RUNE_LAST*2-1)), " and ")); + //sprint(other, strcat(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n")); + + bprint("^3", other.netname, "^7 has picked up ", + RuneName(self.runes & (RUNE_LAST*2-1)), "^7 and "); + bprint(RuneName(self.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"); +} + +void rune_respawn() +{ + rune_unmark_spot(self); + if(rune_spawn_somewhere(self)) + { + self.solid = SOLID_TRIGGER; + self.touch = rune_touch; + self.think = rune_respawn; + self.nextthink = time + autocvar_g_runematch_shuffletime;//30 + random()*5; // fixme: cvar + } + else + { + // try again later + self.think = rune_respawn; + self.nextthink = time; + } +} + +entity FindRune(entity own, string clname, float r) +{ + entity rune; + float _count, c; + + c = _count = 0; + rune = world; + + do + { + rune = find(rune, classname, clname); + if(!rune) + rune = find(rune, classname, clname); + if(!rune) + break; + if(rune.owner == own) + { + _count = _count + 1; + if(_count >= r) + return rune; + if(r <= 1) + return rune; + } + c = c + 1; + }while(c < 30); + return world; +} + + +void DropRune(entity pl, entity e) +{ + //entity pl; + + //pl = e.owner; + // detach from player + setattachment(e, world, ""); + e.owner = world; + e.enemy.owner = world; + // don't instantly touch player again + e.wait = time + 1; // "notouch" time + e.movetype = MOVETYPE_TOSS; + e.solid = SOLID_TRIGGER; + // reposition itself if not picked up soon + e.think = rune_respawn; + e.nextthink = time + autocvar_g_runematch_respawntime;//15 + random()*5; // fixme: cvar + e.touch = rune_touch; + + pl.runes = pl.runes - (pl.runes & (e.runes | e.enemy.runes)); + + // toss from player + setorigin(e, pl.origin + '0 0 10'); + e.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom(); + + + bprint("^3", pl.netname, "^7 has lost ", + RuneName(e.runes & (RUNE_LAST*2-1)), "^7 and "); + bprint(RuneName(e.enemy.runes & (CURSE_WEAK | CURSE_VULNER | CURSE_VENOM | CURSE_SLOW | CURSE_EMPATHY)), "\n"); +} + +float RuneMatchesCurse(float r, float c) +{ + float cr; + if(r & RUNE_STRENGTH) + cr = CURSE_WEAK; + else if(r & RUNE_DEFENSE) + cr = CURSE_VULNER; + else if(r & RUNE_REGEN) + cr = CURSE_VENOM; + else if(r & RUNE_SPEED) + cr = CURSE_SLOW; + else if(r & RUNE_VAMPIRE) + cr = CURSE_EMPATHY; + else return FALSE; // fixme: error? + + if(c & cr) + return TRUE; + return FALSE; +} + +// player died, drop runes +// each rune should pair up with a random curse and then be tossed from the player +void DropAllRunes(entity pl) +{ + entity rune, curse; + float rcount, ccount, r, c, rand, prevent_same, numtodrop, tries; + + entity curse1, rune1, curse2, rune2; + + rcount = ccount = r = c = 0; + rune = find(world, classname, "rune"); + while(rune) + { + if(rune.owner == pl) + rcount = rcount + 1; + rune = find(rune, classname, "rune"); + } + curse = find(world, classname, "curse"); + while(curse) + { + if(curse.owner == pl) + ccount = ccount + 1; + curse = find(curse, classname, "curse"); + } + + numtodrop = autocvar_g_runematch_drop_runes_max; + prevent_same = !autocvar_g_runematch_allow_same; + + do + { + rune = find(rune, classname, "rune"); + if(!rune) + break; + if(rune.owner != pl) + continue; + + + // find a random curse + tries = 15; + if(ccount > 1 && prevent_same) + { + // avoid pairing runes and curses that match each other + do{ + rand = floor(random()*ccount) + 1; + curse = FindRune(pl, "curse", rand); + tries = tries - 1; + }while(RuneMatchesCurse(rune.runes, curse.runes) && tries > 0); + if(tries <= 0) + { + bprint("warning: couldn't prevent same rune\n"); + } + } + else + { + rand = floor(random()*ccount) + 1; + curse = FindRune(pl, "curse", rand); + } + + if(!curse) + error("Couldn't fine curse to bind rune to\n"); + + // pair rune and curse + + rune1 = rune; + curse1 = curse; + rune2 = curse1.enemy; + curse2 = rune1.enemy; + + if(rune1 != rune2) // not already attached to each other + { + rune1.enemy = curse1; + curse1.enemy = rune1; + setattachment(curse1, rune1, ""); + rune2.enemy = curse2; + curse2.enemy = rune2; + setattachment(curse2, rune2, ""); + //DropRune(pl, rune2); + //ccount = ccount - 1; + //rcount = rcount - 1; + } + DropRune(pl, rune1); + + if(numtodrop <=0) + { + rune1.think = rune_respawn; + rune1.nextthink = time; + } + + numtodrop = numtodrop - 1; + + ccount = ccount - 1; + rcount = rcount - 1; + + }while(rune); +} + +void rune_reset() +{ + if(self.owner) + if(self.owner.classname != "runematch_spawn_point") + DropAllRunes(self.owner); + rune_respawn(); +} + +void spawn_runes() +{ + float rn, cs, runes_used, curses_used, prevent_same, numrunes; + entity e; + + if(self) + remove(self); + + // fixme: instead of placing them all now, why not + // simply create them all and let them call rune_respawn() as their think? + + runes_used = 0; + curses_used = 0; + + prevent_same = !autocvar_g_runematch_allow_same; + numrunes = RUNE_COUNT; + + while(numrunes > 0) + { + RandomSelection_Init(); + for(rn = RUNE_FIRST; rn <= RUNE_LAST; rn *= 2) + if not(runes_used & rn) + RandomSelection_Add(world, rn, string_null, 1, 1); + rn = RandomSelection_chosen_float; + + RandomSelection_Init(); + for(cs = CURSE_FIRST; cs <= CURSE_LAST; cs *= 2) + if not(curses_used & cs) + if not(prevent_same && cs == RuneMatchesCurse(rn, cs)) + RandomSelection_Add(world, cs, string_null, 1, 1); + cs = RandomSelection_chosen_float; + + if(!rn || !cs) + error("No rune/curse left"); + + runes_used |= rn; + curses_used |= cs; + + e = spawn(); + e.runes = rn; + e.classname = "rune"; + e.touch = rune_touch; + e.think = rune_respawn; + e.nextthink = time; + e.movetype = MOVETYPE_TOSS; + e.solid = SOLID_TRIGGER; + e.flags = FL_ITEM; + e.reset = rune_reset; + setmodel(e, "models/runematch/rune.mdl"); // precision set below + setsize(e, '0 0 -35', '0 0 0'); + + e.enemy = spawn(); + e.enemy.enemy = e; + e.enemy.classname = "curse"; + e.enemy.runes = cs; + //e.enemy.avelocity = '300 500 200'; + setmodel(e.enemy, "models/runematch/curse.mdl"); // precision set below + setorigin(e, '0 0 0'); + setattachment(e.enemy, e, ""); + + e.colormod = RuneColormod(rn); + e.enemy.colormod = RuneColormod(cs); + + e.alpha = e.enemy.alpha = autocvar_g_runematch_rune_alpha;//0.78; + e.effects = e.enemy.effects = autocvar_g_runematch_rune_effects | EF_LOWPRECISION;//EF_ADDITIVE;// | EF_FULLBRIGHT; + + //e.glow_size = e.enemy.glow_size = cvar("g_runematch_rune_glow_size"); + //e.glow_color = e.enemy.glow_color = cvar("g_runematch_rune_glow_color"); + + //rn = RUNE_FIRST; + //cs = CURSE_FIRST; + + numrunes = numrunes - 1; + } +} + +void runematch_init() +{ + if(!g_runematch) + return; + + entity e; + e = spawn(); + e.think = spawn_runes; + e.nextthink = time + 0.1; +} + + +float runematch_point_time; + +// give points to players who are holding runes +void RuneMatchGivePoints() +{ + entity rune; + + if(!g_runematch || !autocvar_g_runematch_pointamt) + return; + + if(gameover) + return; + + if(runematch_point_time > time) + return; + + runematch_point_time = time + autocvar_g_runematch_pointrate; + + rune = world; + do + { + rune = find(rune, classname, "rune"); + if(!rune) + return; + + if(rune.owner.classname == "player") + { + UpdateFrags(rune.owner, autocvar_g_runematch_pointamt); + } + }while(rune); +} + +float RunematchHandleFrags(entity attacker, entity targ, float f) +{ + entity head; + float arunes, trunes, newfrags; + + if(f <= 0) + return f; + if(attacker == targ) + return f; + + arunes = trunes = 0; + + head = find(world, classname, "rune"); + while(head) + { + if(head.owner == attacker) + { + arunes = arunes + 1; + } + else if(head.owner == targ) + { + trunes = trunes + 1; + } + + head = find(head, classname, "rune"); + } + + if(!arunes && !trunes) + return f - 1 + autocvar_g_runematch_frags_norune; // don't give points to players when no runes are involved. + + newfrags = 0; + if(arunes) + { // got a kill while holding runes + newfrags = newfrags + autocvar_g_runematch_frags_killedby_runeholder;//5; + } + if(trunes) + { // killed an enemy holding runes + newfrags = newfrags + autocvar_g_runematch_frags_killed_runeholder;//5; + } + if(newfrags) + f = f - 1 + newfrags; + + return f; +}