Convert more files to pure Objective-C
FossilOrigin-Name: b250dfa8d49dc6cf7dd86d6c7836733467c87769e3cedaa4678b94d411b7be12
This commit is contained in:
parent
565a845aaf
commit
b4d52ea3ec
12 changed files with 75 additions and 76 deletions
528
src/clientgame.m
Normal file
528
src/clientgame.m
Normal file
|
@ -0,0 +1,528 @@
|
|||
// clientgame.cpp: core game related stuff
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
#import "Entity.h"
|
||||
#import "OFString+Cube.h"
|
||||
|
||||
int nextmode = 0; // nextmode becomes gamemode after next map load
|
||||
VAR(gamemode, 1, 0, 0);
|
||||
|
||||
static void
|
||||
mode(int n)
|
||||
{
|
||||
addmsg(1, 2, SV_GAMEMODE, nextmode = n);
|
||||
}
|
||||
COMMAND(mode, ARG_1INT)
|
||||
|
||||
bool intermission = false;
|
||||
|
||||
DynamicEntity *player1; // our client
|
||||
OFMutableArray *players; // other clients
|
||||
|
||||
void
|
||||
initPlayers()
|
||||
{
|
||||
player1 = newdynent();
|
||||
players = [[OFMutableArray alloc] init];
|
||||
}
|
||||
|
||||
VARP(sensitivity, 0, 10, 10000);
|
||||
VARP(sensitivityscale, 1, 1, 10000);
|
||||
VARP(invmouse, 0, 0, 1);
|
||||
|
||||
int lastmillis = 0;
|
||||
int curtime = 10;
|
||||
OFString *clientmap;
|
||||
|
||||
OFString *
|
||||
getclientmap()
|
||||
{
|
||||
return clientmap;
|
||||
}
|
||||
|
||||
void
|
||||
resetmovement(DynamicEntity *d)
|
||||
{
|
||||
d.k_left = false;
|
||||
d.k_right = false;
|
||||
d.k_up = false;
|
||||
d.k_down = false;
|
||||
d.jumpnext = false;
|
||||
d.strafe = 0;
|
||||
d.move = 0;
|
||||
}
|
||||
|
||||
// reset player state not persistent accross spawns
|
||||
void
|
||||
spawnstate(DynamicEntity *d)
|
||||
{
|
||||
resetmovement(d);
|
||||
d.vel = OFMakeVector3D(0, 0, 0);
|
||||
d.onfloor = false;
|
||||
d.timeinair = 0;
|
||||
d.health = 100;
|
||||
d.armour = 50;
|
||||
d.armourtype = A_BLUE;
|
||||
d.quadmillis = 0;
|
||||
d.lastattackgun = d.gunselect = GUN_SG;
|
||||
d.gunwait = 0;
|
||||
d.attacking = false;
|
||||
d.lastaction = 0;
|
||||
loopi(NUMGUNS) d.ammo[i] = 0;
|
||||
d.ammo[GUN_FIST] = 1;
|
||||
if (m_noitems) {
|
||||
d.gunselect = GUN_RIFLE;
|
||||
d.armour = 0;
|
||||
if (m_noitemsrail) {
|
||||
d.health = 1;
|
||||
d.ammo[GUN_RIFLE] = 100;
|
||||
} else {
|
||||
if (gamemode == 12) {
|
||||
// eihrul's secret "instafist" mode
|
||||
d.gunselect = GUN_FIST;
|
||||
return;
|
||||
}
|
||||
d.health = 256;
|
||||
if (m_tarena) {
|
||||
int gun1 = rnd(4) + 1;
|
||||
baseammo(d.gunselect = gun1);
|
||||
for (;;) {
|
||||
int gun2 = rnd(4) + 1;
|
||||
if (gun1 != gun2) {
|
||||
baseammo(gun2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (m_arena) {
|
||||
// insta arena
|
||||
d.ammo[GUN_RIFLE] = 100;
|
||||
} else {
|
||||
// efficiency
|
||||
loopi(4) baseammo(i + 1);
|
||||
d.gunselect = GUN_CG;
|
||||
}
|
||||
d.ammo[GUN_CG] /= 2;
|
||||
}
|
||||
} else
|
||||
d.ammo[GUN_SG] = 5;
|
||||
}
|
||||
|
||||
DynamicEntity *
|
||||
newdynent() // create a new blank player or monster
|
||||
{
|
||||
DynamicEntity *d = [[DynamicEntity alloc] init];
|
||||
d.o = OFMakeVector3D(0, 0, 0);
|
||||
d.yaw = 270;
|
||||
d.pitch = 0;
|
||||
d.roll = 0;
|
||||
d.maxspeed = 22;
|
||||
d.outsidemap = false;
|
||||
d.inwater = false;
|
||||
d.radius = 1.1f;
|
||||
d.eyeheight = 3.2f;
|
||||
d.aboveeye = 0.7f;
|
||||
d.frags = 0;
|
||||
d.plag = 0;
|
||||
d.ping = 0;
|
||||
d.lastupdate = lastmillis;
|
||||
d.enemy = NULL;
|
||||
d.monsterstate = 0;
|
||||
d.name = d.team = @"";
|
||||
d.blocked = false;
|
||||
d.lifesequence = 0;
|
||||
d.state = CS_ALIVE;
|
||||
spawnstate(d);
|
||||
return d;
|
||||
}
|
||||
|
||||
void
|
||||
respawnself()
|
||||
{
|
||||
spawnplayer(player1);
|
||||
showscores(false);
|
||||
}
|
||||
|
||||
static void
|
||||
arenacount(
|
||||
DynamicEntity *d, int *alive, int *dead, OFString **lastteam, bool *oneteam)
|
||||
{
|
||||
if (d.state != CS_DEAD) {
|
||||
(*alive)++;
|
||||
if (![*lastteam isEqual:d.team])
|
||||
*oneteam = false;
|
||||
*lastteam = d.team;
|
||||
} else
|
||||
(*dead)++;
|
||||
}
|
||||
|
||||
int arenarespawnwait = 0;
|
||||
int arenadetectwait = 0;
|
||||
|
||||
void
|
||||
arenarespawn()
|
||||
{
|
||||
if (arenarespawnwait) {
|
||||
if (arenarespawnwait < lastmillis) {
|
||||
arenarespawnwait = 0;
|
||||
conoutf(@"new round starting... fight!");
|
||||
respawnself();
|
||||
}
|
||||
} else if (arenadetectwait == 0 || arenadetectwait < lastmillis) {
|
||||
arenadetectwait = 0;
|
||||
int alive = 0, dead = 0;
|
||||
OFString *lastteam = nil;
|
||||
bool oneteam = true;
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
arenacount(
|
||||
player, &alive, &dead, &lastteam, &oneteam);
|
||||
arenacount(player1, &alive, &dead, &lastteam, &oneteam);
|
||||
if (dead > 0 && (alive <= 1 || (m_teammode && oneteam))) {
|
||||
conoutf(
|
||||
@"arena round is over! next round in 5 seconds...");
|
||||
if (alive)
|
||||
conoutf(
|
||||
@"team %s is last man standing", lastteam);
|
||||
else
|
||||
conoutf(@"everyone died!");
|
||||
arenarespawnwait = lastmillis + 5000;
|
||||
arenadetectwait = lastmillis + 10000;
|
||||
player1.roll = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern int democlientnum;
|
||||
|
||||
void
|
||||
otherplayers()
|
||||
{
|
||||
[players enumerateObjectsUsingBlock:^(id player, size_t i, bool *stop) {
|
||||
if (player == [OFNull null])
|
||||
return;
|
||||
|
||||
const int lagtime = lastmillis - [player lastupdate];
|
||||
if (lagtime > 1000 && [player state] == CS_ALIVE) {
|
||||
[player setState:CS_LAGGED];
|
||||
return;
|
||||
}
|
||||
|
||||
if (lagtime && [player state] != CS_DEAD &&
|
||||
(!demoplayback || i != democlientnum))
|
||||
// use physics to extrapolate player position
|
||||
moveplayer(player, 2, false);
|
||||
}];
|
||||
}
|
||||
|
||||
void
|
||||
respawn()
|
||||
{
|
||||
if (player1.state == CS_DEAD) {
|
||||
player1.attacking = false;
|
||||
if (m_arena) {
|
||||
conoutf(@"waiting for new round to start...");
|
||||
return;
|
||||
}
|
||||
if (m_sp) {
|
||||
nextmode = gamemode;
|
||||
changemap(clientmap);
|
||||
return;
|
||||
} // if we die in SP we try the same map again
|
||||
respawnself();
|
||||
}
|
||||
}
|
||||
|
||||
int sleepwait = 0;
|
||||
static OFString *sleepcmd = nil;
|
||||
void
|
||||
sleepf(OFString *msec, OFString *cmd)
|
||||
{
|
||||
sleepwait = msec.cube_intValue + lastmillis;
|
||||
sleepcmd = cmd;
|
||||
}
|
||||
COMMANDN(sleep, sleepf, ARG_2STR)
|
||||
|
||||
void
|
||||
updateworld(int millis) // main game update loop
|
||||
{
|
||||
if (lastmillis) {
|
||||
curtime = millis - lastmillis;
|
||||
if (sleepwait && lastmillis > sleepwait) {
|
||||
sleepwait = 0;
|
||||
execute(sleepcmd, true);
|
||||
}
|
||||
physicsframe();
|
||||
checkquad(curtime);
|
||||
if (m_arena)
|
||||
arenarespawn();
|
||||
moveprojectiles((float)curtime);
|
||||
demoplaybackstep();
|
||||
if (!demoplayback) {
|
||||
if (getclientnum() >= 0)
|
||||
// only shoot when connected to server
|
||||
shoot(player1, &worldpos);
|
||||
// do this first, so we have most accurate information
|
||||
// when our player moves
|
||||
gets2c();
|
||||
}
|
||||
otherplayers();
|
||||
if (!demoplayback) {
|
||||
monsterthink();
|
||||
if (player1.state == CS_DEAD) {
|
||||
if (lastmillis - player1.lastaction < 2000) {
|
||||
player1.move = player1.strafe = 0;
|
||||
moveplayer(player1, 10, false);
|
||||
} else if (!m_arena && !m_sp &&
|
||||
lastmillis - player1.lastaction > 10000)
|
||||
respawn();
|
||||
} else if (!intermission) {
|
||||
moveplayer(player1, 20, true);
|
||||
checkitems();
|
||||
}
|
||||
// do this last, to reduce the effective frame lag
|
||||
c2sinfo(player1);
|
||||
}
|
||||
}
|
||||
lastmillis = millis;
|
||||
}
|
||||
|
||||
// brute force but effective way to find a free spawn spot in the map
|
||||
void
|
||||
entinmap(DynamicEntity *d)
|
||||
{
|
||||
loopi(100) // try max 100 times
|
||||
{
|
||||
float dx = (rnd(21) - 10) / 10.0f * i; // increasing distance
|
||||
float dy = (rnd(21) - 10) / 10.0f * i;
|
||||
OFVector3D old = d.o;
|
||||
d.o = OFMakeVector3D(d.o.x + dx, d.o.y + dy, d.o.z);
|
||||
if (collide(d, true, 0, 0))
|
||||
return;
|
||||
d.o = old;
|
||||
}
|
||||
conoutf(
|
||||
@"can't find entity spawn spot! (%d, %d)", (int)d.o.x, (int)d.o.y);
|
||||
// leave ent at original pos, possibly stuck
|
||||
}
|
||||
|
||||
int spawncycle = -1;
|
||||
int fixspawn = 2;
|
||||
|
||||
// place at random spawn. also used by monsters!
|
||||
void
|
||||
spawnplayer(DynamicEntity *d)
|
||||
{
|
||||
int r = fixspawn-- > 0 ? 4 : rnd(10) + 1;
|
||||
loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle + 1);
|
||||
if (spawncycle != -1) {
|
||||
d.o = OFMakeVector3D(
|
||||
ents[spawncycle].x, ents[spawncycle].y, ents[spawncycle].z);
|
||||
d.yaw = ents[spawncycle].attr1;
|
||||
d.pitch = 0;
|
||||
d.roll = 0;
|
||||
} else
|
||||
d.o = OFMakeVector3D((float)ssize / 2, (float)ssize / 2, 4);
|
||||
entinmap(d);
|
||||
spawnstate(d);
|
||||
d.state = CS_ALIVE;
|
||||
}
|
||||
|
||||
// movement input code
|
||||
|
||||
#define dir(name, v, d, s, os) \
|
||||
static void name(bool isdown) \
|
||||
{ \
|
||||
player1.s = isdown; \
|
||||
player1.v = isdown ? d : (player1.os ? -(d) : 0); \
|
||||
player1.lastmove = lastmillis; \
|
||||
}
|
||||
|
||||
dir(backward, move, -1, k_down, k_up);
|
||||
dir(forward, move, 1, k_up, k_down);
|
||||
dir(left, strafe, 1, k_left, k_right);
|
||||
dir(right, strafe, -1, k_right, k_left);
|
||||
|
||||
void
|
||||
attack(bool on)
|
||||
{
|
||||
if (intermission)
|
||||
return;
|
||||
if (editmode)
|
||||
editdrag(on);
|
||||
else if ((player1.attacking = on))
|
||||
respawn();
|
||||
}
|
||||
|
||||
void
|
||||
jumpn(bool on)
|
||||
{
|
||||
if (!intermission && (player1.jumpnext = on))
|
||||
respawn();
|
||||
}
|
||||
|
||||
COMMAND(backward, ARG_DOWN)
|
||||
COMMAND(forward, ARG_DOWN)
|
||||
COMMAND(left, ARG_DOWN)
|
||||
COMMAND(right, ARG_DOWN)
|
||||
COMMANDN(jump, jumpn, ARG_DOWN)
|
||||
COMMAND(attack, ARG_DOWN)
|
||||
COMMAND(showscores, ARG_DOWN)
|
||||
|
||||
void
|
||||
fixplayer1range()
|
||||
{
|
||||
const float MAXPITCH = 90.0f;
|
||||
if (player1.pitch > MAXPITCH)
|
||||
player1.pitch = MAXPITCH;
|
||||
if (player1.pitch < -MAXPITCH)
|
||||
player1.pitch = -MAXPITCH;
|
||||
while (player1.yaw < 0.0f)
|
||||
player1.yaw += 360.0f;
|
||||
while (player1.yaw >= 360.0f)
|
||||
player1.yaw -= 360.0f;
|
||||
}
|
||||
|
||||
void
|
||||
mousemove(int dx, int dy)
|
||||
{
|
||||
if (player1.state == CS_DEAD || intermission)
|
||||
return;
|
||||
const float SENSF = 33.0f; // try match quake sens
|
||||
player1.yaw += (dx / SENSF) * (sensitivity / (float)sensitivityscale);
|
||||
player1.pitch -= (dy / SENSF) *
|
||||
(sensitivity / (float)sensitivityscale) * (invmouse ? -1 : 1);
|
||||
fixplayer1range();
|
||||
}
|
||||
|
||||
// damage arriving from the network, monsters, yourself, all ends up here.
|
||||
|
||||
void
|
||||
selfdamage(int damage, int actor, DynamicEntity *act)
|
||||
{
|
||||
if (player1.state != CS_ALIVE || editmode || intermission)
|
||||
return;
|
||||
damageblend(damage);
|
||||
demoblend(damage);
|
||||
// let armour absorb when possible
|
||||
int ad = damage * (player1.armourtype + 1) * 20 / 100;
|
||||
if (ad > player1.armour)
|
||||
ad = player1.armour;
|
||||
player1.armour -= ad;
|
||||
damage -= ad;
|
||||
float droll = damage / 0.5f;
|
||||
player1.roll += player1.roll > 0
|
||||
? droll
|
||||
: (player1.roll < 0
|
||||
? -droll
|
||||
: (rnd(2) ? droll
|
||||
: -droll)); // give player a kick depending
|
||||
// on amount of damage
|
||||
if ((player1.health -= damage) <= 0) {
|
||||
if (actor == -2) {
|
||||
conoutf(@"you got killed by %@!", act.name);
|
||||
} else if (actor == -1) {
|
||||
actor = getclientnum();
|
||||
conoutf(@"you suicided!");
|
||||
addmsg(1, 2, SV_FRAGS, --player1.frags);
|
||||
} else {
|
||||
DynamicEntity *a = getclient(actor);
|
||||
if (a != nil) {
|
||||
if (isteam(a.team, player1.team))
|
||||
conoutf(@"you got fragged by a "
|
||||
@"teammate (%@)",
|
||||
a.name);
|
||||
else
|
||||
conoutf(
|
||||
@"you got fragged by %@", a.name);
|
||||
}
|
||||
}
|
||||
showscores(true);
|
||||
addmsg(1, 2, SV_DIED, actor);
|
||||
player1.lifesequence++;
|
||||
player1.attacking = false;
|
||||
player1.state = CS_DEAD;
|
||||
player1.pitch = 0;
|
||||
player1.roll = 60;
|
||||
playsound(S_DIE1 + rnd(2), NULL);
|
||||
spawnstate(player1);
|
||||
player1.lastaction = lastmillis;
|
||||
} else
|
||||
playsound(S_PAIN6, NULL);
|
||||
}
|
||||
|
||||
void
|
||||
timeupdate(int timeremain)
|
||||
{
|
||||
if (!timeremain) {
|
||||
intermission = true;
|
||||
player1.attacking = false;
|
||||
conoutf(@"intermission:");
|
||||
conoutf(@"game has ended!");
|
||||
showscores(true);
|
||||
} else {
|
||||
conoutf(@"time remaining: %d minutes", timeremain);
|
||||
}
|
||||
}
|
||||
|
||||
DynamicEntity *
|
||||
getclient(int cn) // ensure valid entity
|
||||
{
|
||||
if (cn < 0 || cn >= MAXCLIENTS) {
|
||||
neterr(@"clientnum");
|
||||
return nil;
|
||||
}
|
||||
while (cn >= players.count)
|
||||
[players addObject:[OFNull null]];
|
||||
return (players[cn] != [OFNull null] ? players[cn]
|
||||
: (players[cn] = newdynent()));
|
||||
}
|
||||
|
||||
void
|
||||
setclient(int cn, id client)
|
||||
{
|
||||
if (cn < 0 || cn >= MAXCLIENTS)
|
||||
neterr(@"clientnum");
|
||||
while (cn >= players.count)
|
||||
[players addObject:[OFNull null]];
|
||||
players[cn] = client;
|
||||
}
|
||||
|
||||
void
|
||||
initclient()
|
||||
{
|
||||
clientmap = @"";
|
||||
initclientnet();
|
||||
}
|
||||
|
||||
void
|
||||
startmap(OFString *name) // called just after a map load
|
||||
{
|
||||
if (netmapstart() && m_sp) {
|
||||
gamemode = 0;
|
||||
conoutf(@"coop sp not supported yet");
|
||||
}
|
||||
sleepwait = 0;
|
||||
monsterclear();
|
||||
projreset();
|
||||
spawncycle = -1;
|
||||
spawnplayer(player1);
|
||||
player1.frags = 0;
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
[player setFrags:0];
|
||||
resetspawns();
|
||||
clientmap = name;
|
||||
if (editmode)
|
||||
toggleedit();
|
||||
setvar(@"gamespeed", 100);
|
||||
setvar(@"fog", 180);
|
||||
setvar(@"fogcolour", 0x8099B3);
|
||||
showscores(false);
|
||||
intermission = false;
|
||||
Cube.sharedInstance.framesInMap = 0;
|
||||
conoutf(@"game mode is %@", modestr(gamemode));
|
||||
}
|
||||
|
||||
COMMANDN(map, changemap, ARG_1STR)
|
Loading…
Add table
Add a link
Reference in a new issue