1 #include "game.h" 2 namespace stf 3 { 4 stfstate st; 5 insideflag(const stfstate::flag & b,gameent * d)6 bool insideflag(const stfstate::flag &b, gameent *d) 7 { 8 return st.insideflag(b, d->feetpos()); 9 } 10 preload()11 void preload() 12 { 13 loopi(TEAM_MAX) loadmodel(teamtype[i].flag, -1, true); 14 } 15 render()16 void render() 17 { 18 loopv(st.flags) 19 { 20 stfstate::flag &b = st.flags[i]; 21 if(!entities::ents.inrange(b.ent)) continue; 22 const char *flagname = teamtype[b.owner].flag; 23 rendermodel(&entities::ents[b.ent]->light, flagname, ANIM_MAPMODEL|ANIM_LOOP, b.o, entities::ents[b.ent]->attrs[2], entities::ents[b.ent]->attrs[3], 0, MDL_SHADOW|MDL_CULL_VFC|MDL_CULL_OCCLUDED); 24 int attack = b.enemy ? b.enemy : b.owner, defend = b.owner ? b.owner : b.enemy; 25 if(b.enemy && b.owner) 26 formatstring(b.info)("<super>\fs%s%s\fS vs. \fs%s%s\fS", teamtype[b.owner].chat, teamtype[b.owner].name, teamtype[b.enemy].chat, teamtype[b.enemy].name); 27 else formatstring(b.info)("<super>\fs%s%s\fS", teamtype[defend].chat, teamtype[defend].name); 28 float occupy = attack ? (!b.owner || b.enemy ? clamp(b.converted/float((!stfstyle && b.owner ? 2 : 1)*stfoccupy), 0.f, 1.f) : 1.f) : 0.f; 29 vec above = b.o; 30 above.z += enttype[FLAG].radius*2/3; 31 part_text(above, b.info); 32 above.z += 2.5f; 33 if(b.enemy) 34 { 35 part_icon(above, textureload(hud::progresstex, 3), 4, 1, 0, 0, 1, teamtype[b.enemy].colour, 0, occupy); 36 part_icon(above, textureload(hud::progresstex, 3), 4, 1, 0, 0, 1, teamtype[b.owner].colour, occupy, 1-occupy); 37 } 38 else part_icon(above, textureload(hud::progresstex, 3), 4, 1, 0, 0, 1, teamtype[b.owner].colour); 39 defformatstring(str)("%d%%", int(occupy*100.f)); part_textcopy(above, str); 40 } 41 } 42 43 adddynlights()44 void adddynlights() 45 { 46 loopv(st.flags) 47 { 48 stfstate::flag &f = st.flags[i]; 49 if(!entities::ents.inrange(f.ent)) continue; 50 adddynlight(vec(f.o).add(vec(0, 0, enttype[FLAG].radius)), enttype[FLAG].radius*1.5f, 51 vec((teamtype[f.owner].colour>>16), ((teamtype[f.owner].colour>>8)&0xFF), (teamtype[f.owner].colour&0xFF)).div(255.f)); 52 } 53 } 54 drawblips(int w,int h,float blend)55 void drawblips(int w, int h, float blend) 56 { 57 loopv(st.flags) 58 { 59 stfstate::flag &f = st.flags[i]; 60 vec dir(f.o); dir.sub(camera1->o); 61 int colour = teamtype[f.enemy && lastmillis%600 >= 400 ? f.enemy : f.owner].colour; 62 float r = (colour>>16)/255.f, g = ((colour>>8)&0xFF)/255.f, b = (colour&0xFF)/255.f, fade = blend*hud::radarflagblend; 63 if(f.owner != game::player1->team && f.enemy != game::player1->team) 64 { 65 float dist = dir.magnitude(), 66 diff = dist <= hud::radarrange() ? clamp(1.f-(dist/hud::radarrange()), 0.f, 1.f) : 0.f; 67 fade *= diff*0.5f; 68 } 69 dir.rotate_around_z(-camera1->yaw*RAD); dir.normalize(); 70 const char *tex = f.hasflag ? hud::arrowtex : hud::flagtex; 71 float size = hud::radarflagsize*(f.hasflag ? 2 : 1); 72 if(hud::radarflagnames > (f.hasflag ? 0 : 1)) 73 { 74 float occupy = !f.owner || f.enemy ? clamp(f.converted/float((!stfstyle && f.owner ? 2 : 1) * stfoccupy), 0.f, 1.f) : 1.f; 75 bool overthrow = f.owner && f.enemy == game::player1->team; 76 if(occupy < 1.f) hud::drawblip(tex, 3, w, h, size, fade, dir, r, g, b, "radar", "%s%d%%", f.hasflag ? (overthrow ? "\fo" : (occupy < 1.f ? "\fy" : "\fg")) : teamtype[f.owner].chat, int(occupy*100.f)); 77 else hud::drawblip(tex, 3, w, h, size, fade, dir, r, g, b, "radar", "%s%s", f.hasflag ? (overthrow ? "\fo" : (occupy < 1.f ? "\fy" : "\fg")) : teamtype[f.owner].chat, teamtype[f.owner].name); 78 } 79 else hud::drawblip(tex, 3, w, h, size, fade, dir, r, g, b); 80 } 81 } 82 drawlast(int w,int h,int & tx,int & ty,float blend)83 void drawlast(int w, int h, int &tx, int &ty, float blend) 84 { 85 if(game::player1->state == CS_ALIVE && hud::shownotices >= 3) 86 { 87 loopv(st.flags) if(insideflag(st.flags[i], game::player1) && (st.flags[i].owner == game::player1->team || st.flags[i].enemy == game::player1->team)) 88 { 89 stfstate::flag &f = st.flags[i]; 90 pushfont("super"); 91 float occupy = !f.owner || f.enemy ? clamp(f.converted/float((!stfstyle && f.owner ? 2 : 1) * stfoccupy), 0.f, 1.f) : 1.f; 92 bool overthrow = f.owner && f.enemy == game::player1->team; 93 ty += draw_textx("\fzwa%s \fs%s%d%%\fS complete", tx, ty, 255, 255, 255, int(255*blend), TEXT_CENTERED, -1, -1, overthrow ? "Overthrow" : "Secure", overthrow ? "\fo" : (occupy < 1.f ? "\fy" : "\fg"), int(occupy*100.f))*hud::noticescale; 94 popfont(); 95 break; 96 } 97 } 98 } 99 drawinventory(int x,int y,int s,int m,float blend)100 int drawinventory(int x, int y, int s, int m, float blend) 101 { 102 int sy = 0; 103 loopv(st.flags) 104 { 105 if(y-sy-s < m) break; 106 stfstate::flag &f = st.flags[i]; 107 bool hasflag = game::player1->state == CS_ALIVE && insideflag(f, game::player1); 108 if(f.hasflag != hasflag) { f.hasflag = hasflag; f.lasthad = lastmillis-max(1000-(lastmillis-f.lasthad), 0); } 109 int millis = lastmillis-f.lasthad; 110 bool headsup = game::player1->state == CS_SPECTATOR || hud::inventorygame >= (f.owner == game::player1->team || st.flags.length() == 1 ? 1 : 2); 111 if(headsup || f.hasflag || millis <= 1000) 112 { 113 int prevsy = sy, colour = teamtype[f.owner].colour; bool skewed = false; 114 float skew = headsup ? hud::inventoryskew : 0.f, fade = blend*hud::inventoryblend, 115 occupy = f.enemy ? clamp(f.converted/float((!stfstyle && f.owner ? 2 : 1)*stfoccupy), 0.f, 1.f) : (f.owner ? 1.f : 0.f), 116 r = (colour>>16)/255.f, g = ((colour>>8)&0xFF)/255.f, b = (colour&0xFF)/255.f; 117 if(f.hasflag) 118 { 119 skewed = true; 120 skew += (millis <= 1000 ? clamp(float(millis)/1000.f, 0.f, 1.f)*(1.f-skew) : 1.f-skew); 121 if(millis > 1000) 122 { 123 float pc = (millis%1000)/500.f, amt = pc > 1 ? 2.f-pc : pc; 124 fade += (1.f-fade)*amt; 125 } 126 } 127 else if(millis <= 1000) 128 { 129 skewed = true; 130 skew += (1.f-skew)-(clamp(float(millis)/1000.f, 0.f, 1.f)*(1.f-skew)); 131 } 132 sy += hud::drawitem(hud::flagtex, x, y-sy, s, false, r, g, b, fade, skew); 133 if(f.enemy) 134 { 135 int colour2 = teamtype[f.enemy].colour; 136 float r2 = (colour2>>16)/255.f, g2 = ((colour2>>8)&0xFF)/255.f, b2 = (colour2&0xFF)/255.f; 137 hud::drawprogress(x, y-prevsy, 0, occupy, int(s*0.5f), false, r2, g2, b2, fade, skew); 138 hud::drawprogress(x, y-prevsy, occupy, 1-occupy, int(s*0.5f), false, r, g, b, fade, skew, !skewed && headsup ? "sub" : "radar", "%s%d%%", hasflag ? (f.owner && f.enemy == game::player1->team ? "\fo" : (occupy < 1.f ? "\fy" : "\fg")) : "\fw", int(occupy*100.f)); 139 } 140 else if(f.owner) hud::drawitem(hud::teamtex(f.owner), x, y-prevsy, int(s*0.5f), false, 1.f, 1.f, 1.f, fade, skew); 141 } 142 } 143 return sy; 144 } 145 setupflags()146 void setupflags() 147 { 148 st.reset(); 149 loopv(entities::ents) 150 { 151 extentity *e = entities::ents[i]; 152 if(e->type != FLAG || !m_check(e->attrs[3], game::gamemode)) continue; 153 stfstate::flag &b = st.flags.add(); 154 b.o = e->o; 155 defformatstring(alias)("flag_%d", e->attrs[4]); 156 const char *name = getalias(alias); 157 if(name[0]) copystring(b.name, name); 158 else formatstring(b.name)("flag %d", st.flags.length()); 159 b.ent = i; 160 } 161 } 162 sendflags(packetbuf & p)163 void sendflags(packetbuf &p) 164 { 165 putint(p, SV_FLAGS); 166 putint(p, st.flags.length()); 167 loopv(st.flags) 168 { 169 stfstate::flag &b = st.flags[i]; 170 putint(p, int(b.o.x*DMF)); 171 putint(p, int(b.o.y*DMF)); 172 putint(p, int(b.o.z*DMF)); 173 } 174 } 175 updateflag(int i,int owner,int enemy,int converted)176 void updateflag(int i, int owner, int enemy, int converted) 177 { 178 if(!st.flags.inrange(i)) return; 179 stfstate::flag &b = st.flags[i]; 180 if(owner) 181 { 182 if(b.owner != owner) 183 { 184 gameent *d = NULL, *e = NULL; 185 loopi(game::numdynents()) if((e = (gameent *)game::iterdynents(i)) && e->type == ENT_PLAYER && insideflag(b, e)) 186 if((d = e) == game::player1) break; 187 game::announce(S_V_FLAGSECURED, d == game::player1 ? CON_SELF : CON_INFO, d, "\fateam \fs%s%s\fS secured %s", teamtype[owner].chat, teamtype[owner].name, b.name); 188 defformatstring(text)("<super>%s\fzReSECURED", teamtype[owner].chat); 189 part_textcopy(vec(b.o).add(vec(0, 0, enttype[FLAG].radius)), text, PART_TEXT, game::aboveheadfade, 0xFFFFFF, 3, 1, -10); 190 game::spawneffect(PART_FIREBALL, vec(b.o).add(vec(0, 0, enttype[FLAG].radius/2)), teamtype[owner].colour, enttype[FLAG].radius*2); 191 } 192 } 193 else if(b.owner) 194 { 195 gameent *d = NULL, *e = NULL; 196 loopi(game::numdynents()) if((e = (gameent *)game::iterdynents(i)) && e->type == ENT_PLAYER && insideflag(b, e)) 197 if((d = e) == game::player1) break; 198 game::announce(S_V_FLAGOVERTHROWN, d == game::player1 ? CON_SELF : CON_INFO, d, "\fateam \fs%s%s\fS overthrew %s", teamtype[enemy].chat, teamtype[enemy].name, b.name); 199 defformatstring(text)("<super>%s\fzReOVERTHROWN", teamtype[enemy].chat); 200 part_textcopy(vec(b.o).add(vec(0, 0, enttype[FLAG].radius)), text, PART_TEXT, game::aboveheadfade, 0xFFFFFF, 3, 1, -10); 201 game::spawneffect(PART_FIREBALL, vec(b.o).add(vec(0, 0, enttype[FLAG].radius/2)), teamtype[enemy].colour, enttype[FLAG].radius*2); 202 } 203 b.owner = owner; 204 b.enemy = enemy; 205 b.converted = converted; 206 } 207 setscore(int team,int total)208 void setscore(int team, int total) 209 { 210 st.findscore(team).total = total; 211 } 212 aiowner(gameent * d)213 int aiowner(gameent *d) 214 { 215 loopv(st.flags) if(entities::ents.inrange(st.flags[i].ent) && entities::ents[d->aientity]->links.find(st.flags[i].ent) >= 0) 216 return st.flags[i].owner ? st.flags[i].owner : st.flags[i].enemy; 217 return d->team; 218 } 219 aicheck(gameent * d,ai::aistate & b)220 bool aicheck(gameent *d, ai::aistate &b) 221 { 222 return false; 223 } 224 aifind(gameent * d,ai::aistate & b,vector<ai::interest> & interests)225 void aifind(gameent *d, ai::aistate &b, vector<ai::interest> &interests) 226 { 227 if(d->aitype == AI_BOT) 228 { 229 vec pos = d->feetpos(); 230 loopvj(st.flags) 231 { 232 stfstate::flag &f = st.flags[j]; 233 static vector<int> targets; // build a list of others who are interested in this 234 targets.setsizenodelete(0); 235 ai::checkothers(targets, d, ai::AI_S_DEFEND, ai::AI_T_AFFINITY, j, true); 236 gameent *e = NULL; 237 bool regen = !m_regen(game::gamemode, game::mutators) || d->health >= max(maxhealth, extrahealth); 238 loopi(game::numdynents()) if((e = (gameent *)game::iterdynents(i)) && !e->ai && ai::owner(d) == ai::owner(e) && ai::targetable(d, e, false)) 239 { 240 vec ep = e->feetpos(); 241 if(targets.find(e->clientnum) < 0 && ep.squaredist(f.o) <= (enttype[FLAG].radius*enttype[FLAG].radius)) 242 targets.add(e->clientnum); 243 } 244 if((!regen && f.owner == ai::owner(d)) || (targets.empty() && (f.owner != ai::owner(d) || f.enemy))) 245 { 246 ai::interest &n = interests.add(); 247 n.state = ai::AI_S_DEFEND; 248 n.node = entities::closestent(WAYPOINT, f.o, ai::NEARDIST, false); 249 n.target = j; 250 n.targtype = ai::AI_T_AFFINITY; 251 n.score = pos.squaredist(f.o)/(!regen ? 100.f : 1.f); 252 } 253 } 254 } 255 } 256 aidefend(gameent * d,ai::aistate & b)257 bool aidefend(gameent *d, ai::aistate &b) 258 { 259 if(st.flags.inrange(b.target)) 260 { 261 stfstate::flag &f = st.flags[b.target]; 262 bool regen = d->aitype != AI_BOT || !m_regen(game::gamemode, game::mutators) || d->health >= max(maxhealth, extrahealth); 263 int walk = f.enemy && f.enemy != ai::owner(d) ? 1 : 0; 264 if(regen && (!f.enemy && ai::owner(d) == f.owner)) 265 { 266 static vector<int> targets; // build a list of others who are interested in this 267 targets.setsizenodelete(0); 268 ai::checkothers(targets, d, ai::AI_S_DEFEND, ai::AI_T_AFFINITY, b.target, true); 269 if(d->aitype == AI_BOT) 270 { 271 gameent *e = NULL; 272 loopi(game::numdynents()) if((e = (gameent *)game::iterdynents(i)) && !e->ai && ai::owner(d) == ai::owner(e) && ai::targetable(d, e, false)) 273 { 274 vec ep = e->feetpos(); 275 if(targets.find(e->clientnum) < 0 && (ep.squaredist(f.o) <= (enttype[FLAG].radius*enttype[FLAG].radius*4))) 276 targets.add(e->clientnum); 277 } 278 } 279 if(!targets.empty()) 280 { 281 if(lastmillis-b.millis >= (201-d->skill)*33) 282 { 283 d->ai->trywipe = true; // re-evaluate so as not to herd 284 return true; 285 } 286 else walk = 2; 287 } 288 else walk = 1; 289 } 290 return ai::defend(d, b, f.o, !f.enemy ? ai::CLOSEDIST : float(enttype[FLAG].radius), !f.enemy ? ai::NEARDIST : float(enttype[FLAG].radius*(1+walk)), walk); 291 } 292 return false; 293 } 294 aipursue(gameent * d,ai::aistate & b)295 bool aipursue(gameent *d, ai::aistate &b) 296 { 297 b.type = ai::AI_S_DEFEND; 298 return aidefend(d, b); 299 } 300 removeplayer(gameent * d)301 void removeplayer(gameent *d) 302 { 303 } 304 } 305