From 131ef8fe546570dcdd1b0f6862388d3315a9507b Mon Sep 17 00:00:00 2001 From: driAn Date: Sun, 7 Jan 2007 14:09:35 +0000 Subject: [PATCH] no message git-svn-id: svn://svn.code.sf.net/p/actiongame/code/trunk/ac@786 e016b1c5-ed68-44aa-a2f7-31cac88cffde --- source/src/client.cpp | 30 ++++---- source/src/clientgame.cpp | 150 ++++++------------------------------ source/src/clients2c.cpp | 41 ++++++++-- source/src/editing.cpp | 3 +- source/src/entities.cpp | 90 +++++++++++++++++++++- source/src/entity.h | 11 +-- source/src/protocol.h | 2 +- source/src/protos.h | 1 + source/src/renderextras.cpp | 2 +- source/src/server.cpp | 58 ++++++-------- source/vcpp/cube.suo | Bin 174592 -> 174592 bytes 11 files changed, 193 insertions(+), 195 deletions(-) diff --git a/source/src/client.cpp b/source/src/client.cpp index 54c93798..d60c0448 100644 --- a/source/src/client.cpp +++ b/source/src/client.cpp @@ -71,8 +71,7 @@ void changeteam(int team) // force team and respawn c2sinit = false; if(m_ctf) tryflagdrop(); s_strncpy(player1->team, team_string(team), MAXTEAMLEN+1); - player1->lastaction = lastmillis; - player1->state = CS_DEAD; + playerdeath(player1); }; void newteam(char *name) @@ -81,23 +80,20 @@ void newteam(char *name) { if(m_teammode) { - bool checkteamsize = autoteambalance && players.length() >= 1; + bool checkteamsize = autoteambalance && players.length() >= 1; int freeteam = smallerteam(); - if(!strcmp(name, "RVSF") || !strcmp(name, "CLA")) - { - if(!strcmp(name, player1->team)) return; // same team - int team = team_int(name); - if(checkteamsize && team != freeteam) - { - conoutf("\f3the %s team is already full", name); - return; - }; - changeteam(team); - } - else changeteam(checkteamsize ? (uint)freeteam : rnd(2)); // random assignement - } - else return; + if(!team_valid(name)) changeteam(checkteamsize ? (uint)freeteam : rnd(2)); // random assignement + if(!strcmp(name, player1->team)) return; // same team + + int team = team_int(name); + if(checkteamsize && team != freeteam) + { + conoutf("\f3the %s team is already full", name); + return; + }; + changeteam(team); + }; } else conoutf("your team is: %s", player1->team); }; diff --git a/source/src/clientgame.cpp b/source/src/clientgame.cpp index d9696042..a550b6c3 100644 --- a/source/src/clientgame.cpp +++ b/source/src/clientgame.cpp @@ -42,6 +42,17 @@ void setskin(playerent *pl, int skin) pl->skin = max(0, skin % (maxskin+1)); }; +void playerdeath(playerent *pl) +{ + pl->lastaction = lastmillis; + pl->lifesequence++; + pl->attacking = false; + pl->state = CS_DEAD; + pl->oldpitch = pl->pitch; + pl->pitch = 0; + pl->roll = 60; +}; + void spawnstate(playerent *d) // reset player state not persistent accross spawns { d->respawn(); @@ -477,17 +488,11 @@ void selfdamage(int damage, int actor, playerent *act, bool gib, playerent *pl) setscope(false); addmsg(gib ? SV_GIBDIED : SV_DIED, "ri", actor); }; - pl->lifesequence++; - pl->attacking = false; - pl->state = CS_DEAD; - pl->oldpitch = pl->pitch; - pl->pitch = 0; - pl->roll = 60; - playsound(S_DIE1+rnd(2), pl!=player1 ? &pl->o : NULL); - if(gib) addgib(pl); + playerdeath(pl); spawnstate(pl); - pl->lastaction = lastmillis; - if (pl!=player1 || act->type==ENT_BOT) act->frags++; + playsound(S_DIE1+rnd(2), pl!=player1 ? &pl->o : NULL); + if(gib) addgib(pl); + if(pl!=player1 || act->type==ENT_BOT) act->frags++; } else { @@ -549,6 +554,7 @@ void preparectf(bool cleanonly=false) e.spawned = true; if(e.attr2>2) { conoutf("\f3invalid ctf-flag entity (%i)", i); e.attr2 = 0; }; flaginfo &f = flaginfos[e.attr2]; + f.team = e.attr2; f.flag = &e; f.state = CTFF_INBASE; f.originalpos.x = (float) e.x; @@ -602,144 +608,34 @@ void suicide() COMMAND(suicide, ARG_NONE); -// ctf flag actions done by the local player - -void flagpickup() -{ - int flag = team_opposite(team_int(player1->team)); - flaginfo &f = flaginfos[flag]; - if(f.flag) - { - f.flag->spawned = false; - f.state = CTFF_STOLEN; - f.actor = player1; // do this although we don't know if we picked the flag to avoid getting it after a possible respawn - f.ack = false; - addmsg(SV_FLAGPICKUP, "ri", flag); - }; -}; - -void tryflagdrop() -{ - int flag = team_opposite(team_int(player1->team)); - flaginfo &f = flaginfos[flag]; - if(f.state==CTFF_STOLEN && f.actor==player1) - { - f.flag->spawned = false; - f.state = CTFF_DROPPED; - f.ack = false; - addmsg(SV_FLAGDROP, "ri", flag); - }; -}; - -void flagreturn() -{ - int flag = team_int(player1->team); - flaginfo &f = flaginfos[flag]; - if(f.flag) - { - f.flag->spawned = false; - f.ack = false; - addmsg(SV_FLAGRETURN, "ri", flag); - }; -}; - -void flagscore() -{ - int flag = team_opposite(team_int(player1->team)); - flaginfo &f = flaginfos[flag]; - f.ack = false; - addmsg(SV_FLAGSCORE, "ri", flag); -}; - -void flagreset() -{ - int flag = team_opposite(team_int(player1->team)); - flaginfo &f = flaginfos[flag]; - if(f.state==CTFF_STOLEN && f.actor==player1) - { - f.flag->spawned = false; - f.state = CTFF_INBASE; - f.ack = false; - addmsg(SV_FLAGRESET, "ri", flag); - }; -}; - -// flag actions from the net - -void flagstolen(int flag, int action, playerent *actor) -{ - if(actor) - { - flaginfo &f = flaginfos[flag]; - f.actor = actor; - f.flag->spawned = false; - f.ack = true; - flagmsg(flag, action); - }; -}; - -void flagdropped(int flag, int action, short x, short y, short z) -{ - flaginfo &f = flaginfos[flag]; - sqr *dropplace = S_SECURE(x, y); - if(!dropplace) return; - - z -= 4; - float floor = (float) dropplace->floor; - if(z > hdr.waterlevel) // above water - { - if(floor < hdr.waterlevel) z = hdr.waterlevel; // avoid dropping into water - else z = (short) floor; - }; - - f.flag->x = x; - f.flag->y = y; - f.flag->z = z; - f.flag->spawned = true; - f.ack = true; - flagmsg(flag, action); -}; - -void flaginbase(int flag, int action, playerent *actor) -{ - flaginfo &f = flaginfos[flag]; - if(actor) f.actor = actor; - f.flag->x = (ushort) f.originalpos.x; - f.flag->y = (ushort) f.originalpos.y; - f.flag->z = (ushort) f.originalpos.z; - f.flag->spawned = true; - f.ack = true; - flagmsg(flag, action); -}; - // console and audio feedback void flagmsg(int flag, int action) { flaginfo &f = flaginfos[flag]; if(!f.actor || !f.ack) return; - bool ownflag = flag == team_int(player1->team); + bool own = flag == team_int(player1->team); switch(action) { case SV_FLAGPICKUP: { playsound(S_FLAGPICKUP); if(f.actor==player1) conoutf("\f2you got the enemy flag"); - else conoutf("\f2%s got %s flag", f.actor->name, (ownflag ? "your": "the enemy")); + else conoutf("\f2%s got %s flag", f.actor->name, (own ? "your": "the enemy")); break; }; case SV_FLAGDROP: { playsound(S_FLAGDROP); if(f.actor==player1) conoutf("you lost the flag"); - else conoutf("\f2%s lost %s flag", f.actor->name, (ownflag ? "your" : "the enemy")); + else conoutf("\f2%s lost %s flag", f.actor->name, (own ? "your" : "the enemy")); break; }; case SV_FLAGRETURN: { playsound(S_FLAGRETURN); if(f.actor==player1) conoutf("you returned your flag"); - else conoutf("\f2%s returned %s flag", f.actor->name, (ownflag ? "your" : "the enemy")); + else conoutf("\f2%s returned %s flag", f.actor->name, (own ? "your" : "the enemy")); break; }; case SV_FLAGSCORE: @@ -750,7 +646,7 @@ void flagmsg(int flag, int action) conoutf("\f2you scored"); addmsg(SV_FLAGS, "ri", ++player1->flagscore); } - else conoutf("\f2%s scored for %s team", f.actor->name, (ownflag ? "the enemy" : "your")); + else conoutf("\f2%s scored for %s team", f.actor->name, (own ? "the enemy" : "your")); break; }; case SV_FLAGRESET: @@ -763,6 +659,8 @@ void flagmsg(int flag, int action) }; }; +// server administration + void setmaster(char *claim, char *password) { int c = atoi(claim); @@ -786,14 +684,12 @@ void kick(int player) { mastercommand(MCMD_KICK, player); }; void ban(int player) { mastercommand(MCMD_BAN, player); }; void removebans() { mastercommand(MCMD_REMBANS, 0); }; void autoteam(int enable) { mastercommand(MCMD_AUTOTEAM, enable); }; -void shuffle() { mastercommand(MCMD_SHUFFLETEAMS, 0); }; COMMAND(setmaster, ARG_2STR); COMMAND(kick, ARG_1INT); COMMAND(ban, ARG_1INT); COMMAND(removebans, ARG_NONE); COMMAND(autoteam, ARG_1INT); -COMMAND(shuffle, ARG_NONE); struct mline { string cmd; }; static vector mlines; diff --git a/source/src/clients2c.cpp b/source/src/clients2c.cpp index bfc0c0f1..f21e8bcc 100644 --- a/source/src/clients2c.cpp +++ b/source/src/clients2c.cpp @@ -146,8 +146,10 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) changemapserv(text, getint(p)); if(joining && m_arena) { - player1->state = CS_DEAD; - showscores(true); + /* TESTME + player1->state = CS_DEAD; + showscores(true);*/ + playerdeath(player1); }; mapchanged = true; break; @@ -261,6 +263,11 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) { frags = -1; conoutf("you fragged a teammate (%s)", d->name); + if(m_ctf) + { + flaginfo &flag = flaginfos[team_opposite(team_int(d->team))]; + if(flag.state == CTFF_STOLEN && flag.actor == d) playerdeath(player1); // punish for ctf TK + }; } else { @@ -459,9 +466,9 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) case SV_MASTERINFO: { + loopv(players) { players[i]->ismaster = false; } int m = getint(p); - if(m == -1) loopv(players) { players[i]->ismaster = false; } - else + if(m != -1) { playerent *pl = (m == getclientnum() ? player1 : getclient(m)); if(pl) @@ -473,6 +480,29 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) break; }; + case SV_MASTERCMD: + { + int cmd = getint(p), arg = getint(p); + switch(cmd) + { + case MCMD_KICK: + case MCMD_BAN: + { + playerent *pl = (arg == getclientnum() ? NULL : getclient(arg)); + if(pl) conoutf("%s has been %s", pl->name, cmd == MCMD_KICK ? "kicked" : "banned"); + break; + } + case MCMD_REMBANS: + conoutf("bans removed"); + break; + + case MCMD_MASTERMODE: + conoutf("mastermode set to %i", arg); + break; + }; + break; + }; + case SV_FORCETEAM: { changeteam(getint(p)); @@ -481,8 +511,7 @@ void parsemessages(int cn, playerent *d, ucharbuf &p) case SV_AUTOTEAM: { - autoteambalance = getint(p) == 1; - conoutf("autoteam is %s", autoteambalance ? "enabled" : "disabled"); + conoutf("autoteam is %s", (autoteambalance = getint(p) == 1) ? "enabled" : "disabled"); break; }; diff --git a/source/src/editing.cpp b/source/src/editing.cpp index fac22759..daed9dfd 100644 --- a/source/src/editing.cpp +++ b/source/src/editing.cpp @@ -45,8 +45,9 @@ void toggleedit() { resettagareas(); // clear trigger areas to allow them to be edited player1->health = 100; - //put call to clear/restar gamemode + //put call to clear/restart gamemode projreset(); + player1->attacking = false; }; keyrepeat(editmode); selset = false; diff --git a/source/src/entities.cpp b/source/src/entities.cpp index f0fb9328..59950202 100644 --- a/source/src/entities.cpp +++ b/source/src/entities.cpp @@ -38,12 +38,11 @@ void renderentities() if(!&mmi) continue; rendermodel(mmi.name, ANIM_MAPMODEL|ANIM_LOOP, e.attr4, mmi.rad ? (float)mmi.rad : 1.1f, e.x, (float)S(e.x, e.y)->floor+mmi.zoff+e.attr3, e.y, (float)((e.attr1+7)-(e.attr1+7)%15), 0, 10.0f); } - else if(e.type==CTF_FLAG && m_ctf) // EDIT: AH + else if(m_ctf && e.type==CTF_FLAG) // EDIT: AH { flaginfo &f = flaginfos[e.attr2]; - if(f.state==CTFF_STOLEN && f.actor) + if(f.state==CTFF_STOLEN && f.actor == player1) { - if(f.actor == player1) continue; s_sprintfd(path)("pickups/flags/small_%s", team_string(e.attr2)); rendermodel(path, ANIM_FLAG|ANIM_START, 0, 1.1f, f.actor->o.x, f.actor->o.z+0.3f+(sinf(lastmillis/100.0f)+1)/10, f.actor->o.y, lastmillis/2.5f, 0, 120.0f); } @@ -347,3 +346,88 @@ bool intersect(entity *e, vec &from, vec &to, vec *end) // if lineseg hits entit return false; }; // End add by Ricks + +// ctf flag actions done by the local player + +void flagpickup() +{ + flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; + f.flag->spawned = false; + f.state = CTFF_STOLEN; + f.actor = player1; // do this although we don't know if we picked the flag to avoid getting it after a possible respawn + f.ack = false; + addmsg(SV_FLAGPICKUP, "ri", f.team); +}; + +void tryflagdrop() +{ + flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; + if(f.state==CTFF_STOLEN && f.actor==player1) + { + f.flag->spawned = false; + f.state = CTFF_DROPPED; + f.ack = false; + addmsg(SV_FLAGDROP, "ri", f.team); + }; +}; + +void flagreturn() +{ + flaginfo &f = flaginfos[team_int(player1->team)]; + f.flag->spawned = false; + f.ack = false; + addmsg(SV_FLAGRETURN, "ri", f.team); +}; + +void flagscore() +{ + flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; + f.ack = false; + addmsg(SV_FLAGSCORE, "ri", f.team); +}; + +// flag actions from the net + +void flagstolen(int flag, int action, playerent *actor) +{ + if(!actor) return; + flaginfo &f = flaginfos[flag]; + f.actor = actor; + f.flag->spawned = false; + f.ack = true; + flagmsg(flag, action); +}; + +void flagdropped(int flag, int action, short x, short y, short z) +{ + flaginfo &f = flaginfos[flag]; + sqr *dropplace = S_SECURE(x, y); + if(!dropplace) return; + + z -= 4; + float floor = (float) dropplace->floor; + if(z > hdr.waterlevel) // above water + { + if(floor < hdr.waterlevel) z = hdr.waterlevel; // avoid dropping into water + else z = (short) floor; + }; + + f.flag->x = x; + f.flag->y = y; + f.flag->z = z; + f.flag->spawned = true; + f.ack = true; + flagmsg(flag, action); +}; + +void flaginbase(int flag, int action, playerent *actor) +{ + flaginfo &f = flaginfos[flag]; + if(actor) f.actor = actor; + f.flag->x = (ushort) f.originalpos.x; + f.flag->y = (ushort) f.originalpos.y; + f.flag->z = (ushort) f.originalpos.z; + f.flag->spawned = true; + f.ack = true; + flagmsg(flag, action); +}; \ No newline at end of file diff --git a/source/src/entity.h b/source/src/entity.h index 0255bfda..78dd4e98 100644 --- a/source/src/entity.h +++ b/source/src/entity.h @@ -32,9 +32,9 @@ enum { GUN_KNIFE = 0, GUN_PISTOL, GUN_SHOTGUN, GUN_SUBGUN, GUN_SNIPER, GUN_ASSAU #define isteam(a,b) (m_teammode && strcmp(a, b)==0) -#define TEAM_CLA 0 // -#define TEAM_RVSF 1 // -// rb means red/blue +#define TEAM_CLA 0 +#define TEAM_RVSF 1 +#define team_valid(t) (!strcmp(t, "RVSF") || !strcmp(t, "CLA")) #define team_string(t) ((t) ? "RVSF" : "CLA") #define team_int(t) (strcmp((t), "CLA") == 0 ? TEAM_CLA : TEAM_RVSF) #define team_opposite(o) ((o) == TEAM_CLA ? TEAM_RVSF : TEAM_CLA) @@ -224,11 +224,12 @@ enum { CTFF_INBASE = 0, CTFF_STOLEN, CTFF_DROPPED }; struct flaginfo { + int team; entity *flag; playerent *actor; vec originalpos; - int state; // one of the types above - bool ack; // is the data ack'd ? + int state; // one of CTFF_* + bool ack; // data is ackd flaginfo() : flag(0), actor(0), state(CTFF_INBASE), ack(false) {}; }; diff --git a/source/src/protocol.h b/source/src/protocol.h index 64a112ca..dfeec559 100644 --- a/source/src/protocol.h +++ b/source/src/protocol.h @@ -24,7 +24,7 @@ enum SV_CLIENT, }; -enum { MCMD_KICK = 0, MCMD_BAN, MCMD_REMBANS, MCMD_MASTERMODE, MCMD_AUTOTEAM, MCMD_SHUFFLETEAMS }; +enum { MCMD_KICK = 0, MCMD_BAN, MCMD_REMBANS, MCMD_MASTERMODE, MCMD_AUTOTEAM }; #define DMF 16.0f #define DAF 1.0f diff --git a/source/src/protos.h b/source/src/protos.h index b00bc2ea..3ac9244b 100644 --- a/source/src/protos.h +++ b/source/src/protos.h @@ -98,6 +98,7 @@ extern void updateworld(int curtime, int lastmillis); extern void startmap(char *name); extern void changemap(char *name); extern void initclient(); +extern void playerdeath(playerent *pl); extern void spawnplayer(playerent *d); extern void selfdamage(int damage, int actor, playerent *act, bool gib = false, playerent *pl = player1); extern playerent *newplayerent(); diff --git a/source/src/renderextras.cpp b/source/src/renderextras.cpp index 793c03e3..1600b052 100644 --- a/source/src/renderextras.cpp +++ b/source/src/renderextras.cpp @@ -452,7 +452,7 @@ void gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwat // big flag icon flaginfo &f = flaginfos[team_opposite(team_int(player1->team))]; - if(f.state==CTFF_STOLEN && f.actor == player1) + if(f.state==CTFF_STOLEN && f.actor == player1 && f.ack) { glPushMatrix(); glOrtho(0, VIRTW/2, VIRTH/2, 0, -1, 1); diff --git a/source/src/server.cpp b/source/src/server.cpp index 5499eb78..7c5596a5 100644 --- a/source/src/server.cpp +++ b/source/src/server.cpp @@ -439,8 +439,22 @@ void resetvotes() loopv(clients) clients[i]->mapvote[0] = 0; }; +void shuffleteams() +{ + int numplayers = numclients(); + int teamsize[2] = {0, 0}; + loopv(clients) if(clients[i]->type!=ST_EMPTY) + { + int team = rnd(2); + if(teamsize[team] > numplayers/2) team = team_opposite(team); + sendf(i, 1, "rii", SV_FORCETEAM, team); + teamsize[team]++; + }; +}; + void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = true) { + bool lastteammode = m_teammode; smode = newmode; minremain = newtime >= 0 ? newtime : (m_teammode ? 15 : 10); s_strcpy(smapname, newname); @@ -454,6 +468,7 @@ void resetmap(const char *newname, int newmode, int newtime = -1, bool notify = resetscores(); ctfreset(); if(notify) sendf(-1, 1, "risi", SV_MAPCHANGE, smapname, smode); + if(m_teammode && !lastteammode) shuffleteams(); }; void nextcfgset(bool notify = true) // load next maprotation set @@ -514,19 +529,6 @@ bool isbanned(int cn) return false; }; -void shuffleteams() -{ - int numplayers = numclients(); - int teamsize[2] = {0, 0}; - loopv(clients) if(clients[i]->type!=ST_EMPTY) - { - int team = rnd(2); - if(teamsize[team] > numplayers/2) team = team_opposite(team); - sendf(i, 1, "rii", SV_FORCETEAM, team); - teamsize[team]++; - }; -}; - int master() { loopv(clients) if(clients[i]->type!=ST_EMPTY && clients[i]->ismaster) return i; @@ -555,57 +557,45 @@ void setmaster(int client, bool claim, char *pwd = NULL) }; }; -void mastercmd(int sender, int cmd, int a) +bool mastercmd(int sender, int cmd, int a) { - if(!isdedicated) return; - if(clients[sender]->ismaster==false) return; + if(!isdedicated || clients[sender]->ismaster==false) return false; switch(cmd) { case MCMD_KICK: { - if(!valid_client(a)) return; + if(!valid_client(a)) return false; disconnect_client(a, DISC_MKICK); - sendservmsg("player kicked"); break; }; case MCMD_BAN: { - if(!valid_client(a)) return; + if(!valid_client(a)) return false; ban b = { clients[a]->peer->address, lastsec+20*60 }; bans.add(b); disconnect_client(a, DISC_MBAN); - sendservmsg("player banned"); break; }; case MCMD_REMBANS: { if(bans.length()) bans.setsize(0); - sendservmsg("bans removed"); break; }; case MCMD_MASTERMODE: { - if(a < 0 || a >= MM_NUM) return; + if(a < 0 || a >= MM_NUM) return false; mastermode = a; - s_sprintfd(msg)("mastermode set to %i", mastermode); - sendservmsg(msg); break; }; case MCMD_AUTOTEAM: { - if(a < 0 || a > 1) return; - autoteam = a == 1; + if(a < 0 || a > 1) return false; + if((autoteam = a) == 1 && m_teammode) shuffleteams(); sendf(-1, 1, "rii", SV_AUTOTEAM, a); break; }; - case MCMD_SHUFFLETEAMS: - { - if(!m_teammode) return; - shuffleteams(); - sendservmsg("teams were shuffled"); - break; - }; }; + return true; }; int checktype(int type, client *cl) @@ -833,7 +823,7 @@ void process(ENetPacket *packet, int sender, int chan) // sender may be -1 { int cmd = getint(p); int arg = getint(p); - mastercmd(sender, cmd, arg); + if(mastercmd(sender, cmd, arg) && cmd != MCMD_AUTOTEAM) QUEUE_MSG; break; }; diff --git a/source/vcpp/cube.suo b/source/vcpp/cube.suo index dd8d12ebe2704593ffc065a53573d5e6ec7cf8d9..09df49392540f786e6768da5255e4b34a9317cd4 100644 GIT binary patch delta 9017 zcmcIq3sjZWw*KeZ4?(wxfQX6$if9UiiZ6VyLGp=-h;Kqb@QFr7R+_RSD@`l)PiA!+ z<|#9+OqAj`ui4f*9ZM^{dAglW4>IeX(#p)vO*_i{=D*iYYTPljJI0;EZ?Cz&HP`yr zd(E}bccP{5M9aDgXINU_Ba3JE{R=q^!|*;;zs&g*IWIRgHJR~lze-W&N%wFnF%uk- z_316vtL_A|2%yR+H%g2}#;wMDV;S_{Nd4ELGszVRuH&ZT)Z46TwMz9e%!{p-xkiC} zuKrF>sB?H5gHYN#z!yLdB)s!{dc^maziSd6|BQB0!teMdWf+mDY`s4`UAf1(4PyyP zj#%dIj~oM|%w>@)RRx(BBUjQB^>;>%rBLdIBG96yjqmiVKi|&d$sG8*G7&XgK>PoN zmo$-Pb>j_jYFPbUU319vCVM%261C*QM^sCW@=$WUH_`9*=Af3-fZx}C07@zV;(uGB z@e(TkCNNm@PkVb4dp+c(LNlw|xcZ~XIj*i>p}_lKAj7j-hYuj^f$ms`aF{at_I=JH z$*&;#H<;J|qwhYKbIAEM%grrAqRg}}+@^ofJmYvLP)GF@5@-rTM!0uEDMS21u02x!H#{BIq z=#URyrC#;EkvXkH9z~KK1NVPGk4dh-ILTe$T#HJY&h3eTIhVXWrrkDSfJcB$fHWt> zoW6ak^JPhSX?uJJp?*Z8_v&OR*cS8K?YFul^|V>OV@Ma#zJZO*nKQt*z<0p+z-46~ z*pV?xObiqikOvW96l{4&hwBjvC)0U@=3djBtOUPs^_kC+GQE3?`ZUq{w}p}V1Tuyr zwnHe$e*5!k{+&rdsRKmsi6jJ}qi;p_nNj;v_CNJ0~Ao%kTikv(F+Af{+ zlTL~Q_fO5aF_T1%1zJECY1@p8&|I|U+9JI@@7PbB61r$g=2ar}mI(W4t~>NN^RHj^ zs2&PN+CN;U$Y2eIE;2=PqEFSFY&udQ8R+Sno7K#&SaY*A*A;eUnv)f$+hwmjB+W8v zHQ5_6)`m}Ot}D2$n%ki{so+k{y{I`*#xBjh)QoFT_1>428Y1bhXkK>AecJYLGuyW` zcLZTzZDH*wgOB%CBTT>LWA8H6caeNuP6v@Fgu&*t(_F`9T&(8eG$&t<*Ia5dyX!UA z2VwBbQvT5T&-Q-lFyC;dx$Z%9xOrj!IM*0xqXUQDs}s!Eoqg)hzq)_88t*pu{A)mW z*==M*C6p1(1iU~NFbNm|Of_3yYHx4i;}8=|Gwo7Ll(e7##AHAQUJ~jHFV(r|TW;;3 zo7*+w%bx=$fiHklz;QeLhpq!=@` zF2ecvPk(w>PGaj&6m6qTCn((23i*jY-$`+NwztZNOhvC6#+C^$-iRPjVe9K?dX=0b zHjOMax8%lf%_!B#Pj#SpGuuk8mJKRdY$4)s5Fkg5xIL(dF&Yp~7Ry*bBp(n?cB4sv zNGTwkER5NJNChCA90rR3ktKj|vgh0yNHUC-niSWt+N$nI_0qZsUgcIpX@Tt`)cu8` zt&_2|S+V~%^0Mzq{CM|MeCHofoRlavJ|_yqfvRK9H`z;mE}u%KSoXV>ueK%n)7wt@ z8oH9t!Txrt3vcdDU29Xw+e_S92ao_b0T=g2GO;5_^ulk|}jVV-II}!Ev#BLjOX-yqj-ZXMJLrLyVvI4O8a8)|?@=Opn*gMVnbUGF?@=CgOzCXPgMn|lF z&!O8|*r&-u=&oa!U>iz$a#=2Y7%xQ~2l)x`K5zmU1p`?FB39ve+UmdnNrc@K=_0Fq z8qMoss3WE%E(TM%%V7!=Ps&5Zzb6f6R0fv&LhEP_;1)agAIoaIA zV}n#1;+jdiWR47wMvUeXz;)1EXUz=)CqA19lp<0u*nv||#&2813eB>F;y{I`*#xBj(X->|O-I{wvbCP$T=KPwIAvvhI>LZ$z zrSmRyY262!i-!K8=04V(Ec(mPr8k;1*AMzp=pt#Dxzb{Bmis^#>908{Z-C}90^9)m zq#L5iY;7o+r$ZlsaE9hqLm#2-O09v@Db&F=Z%{g~_fTx>x8<7@7@qa<-EI?R%|RA%XP)%G`;VXIi(ux^-?~6S3+(x zJ2ASUlww-6lZ9A-s*C}~0^@+o8%nx4CquCc=F2}QWg6* z(gLTr*|knJrC|yAoSZve4G11Gi5zk}4dTYVSlF%1yL)%y!rLj4{SnG*`ERGC4jwy# zJk|J!@hKqk86fSEeY_D6k$qY?vHJ!PISmBu&Ow)9zMwf7`ZIvYFM!yI{3{R`4iM60 zI6u&4SO=HWOs7*EA&&P^Bj+Ea7;Den6h^#$v?}7WKT-%M?V)tbUqd0zSjiidk$B%3 z2n07bK6M|>^8`~RgL*k#Pj+?yzk|FkCf81vyk@@$;i#p<%1lE_sFt0e)K za|hYi4(t35@+hp0cyqOvXFC*)wGi)Q=qZa zc2Tn3b6;Q>Pol@ZMEDhO4hVF|E-Gds$WZ>66qY}6)fy(a2 znBbI%uFa!(_V=b5t7Z=!aIx=4x`Q{KqtOkoP_g4G9^Vgsx-)e03r{Dawgy>E`-!O8 zP=y|#PFDH>I!YTV!#O^W(x|WaAa`>5VOq>pZ{g>W)0A=L9g8d9B4^-j%{-bMfyw{b zTeMdE(u}FR7g>l|DI6PnagmSrPA%#$TzBE)V=k=#y3c%_^FleV(@8(psL zsQe8d(Q0yjjiKC765}K+S>FD4n$~JI_B5Gk*zY+rjAp|ZIKB;A%$2t7+lCsR=&f@3 zXlIIX^vA?~ZP3()PsvASn$yWed(e#Ry;!#X&uCB!wbj9yL7~@OsT*^C!qnz|2e>LnL!d4^LPk7Ecm)-U-J;<~iGrN6$KFSL*ln z&R^@-LB(+KZt3*tSlu^Dr%&aw-887-JMucMkIrFT)kdqw8_v_{mgeEj$y{|AF4r=& ze`&zwx=a&7>1M=bRlp~+s+#D8Qjx@Cm0HrhANH(CKwm(1opc=zKsXQ>1PtcSmFg6p z1yu^kMkudt8%m;_L+!AqmFrMHhlVV?@)@pbt$eOE=z|LDLTmMwlXDs=oFgLDV)m~k zujPwWKE=nTs!EPONMpI6y-MZAyD<}UJE(LX8?6ehn)d2V;=CU*@qHasjdiG_%2S*l ztI{}oyz&MQh6U`MrrbQTvs%T8ajKBLsnBxbRJ0iqmd<#{FpQC4ip;DbQ+e@qc)UJQ z8C-u`l2pv5LKT6YfeSl}&%TbHX}nH_5X|v#y>2ExIh7anR`vYgLrQLg+kZE=~_FC(hO6A&gI5T8*b0|;6T zvzfqiYtT-ddl6}faN-om4M5~3AegtvX0BZGOO8WhVgHj>xA$L%{>#~7T9gwrpadmq#fI?x#u(|efc~f4&F}9g(KVph_|^{bGQi_ zuK@Bw@s8%id9%5`ZyKq||(b!bzuo4v5GF zQaGteu_%x-%}FiiLU-d*VJy(3IL$$aaS=G#6AV8124zYShK#I;L;^_@OQ-{<{u$3I z^7>hhjrsro^^-T`Q;$FDtF*ZmFQmG^x}d1ix^s}KCU=E2)!@qxinqFCs8SF6UV(qS zG$hRQzmuv@+z>f&<+)Bg!dUi=QYX3VXcfM$*_&W%<#I(bPxd7t%-SA;YD z?iYyf!@ynMgsQ$qkz%C7+@?QHn}_(>t1179gsoAk{k=+iuMiHzz%Q_)=48Jo6y zy1K~z8Oq1r5AminLmlFp4+%MDDnI8GV&BRgr(*x8^Obk)E>UMLokU$Z7ke2s`(s{S ziE`IQI-sQn*cu>Mwr`j;|#ubjgvoSpM8JiyKqXQQdgMX}KiqrY_uTlUmC^%vFz?>-L7T zxlK*6tJ}i4OI@X{oj9QkXECk|;XHAn%1m_!UbM>WUWqiWQiS#CeF`s;SFs9o>eKhC zJ?7CHqIl9mm47Yuvr? zOv_YL>ngmlS7KCUFZ|uzq*XS!Qasvb7=>2Ta)pmOt)(l~TH>sGRj+HRIRBrgy;L@f zpV)+TcP<~>0Q|HFYi5nwMtn=Ts{F%K`Uv|DWB;}X=32U(*RIEcjZen^F?e{JDoVKe zGRL%;YSC)d6w0L@O1*OHtG4_b)jjRpMlCw@vA~wJJ+LLYhG5HDY_{!jy&2ylmUDKf G{r>|??k0Hv delta 10901 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