Convert dynent to a class
FossilOrigin-Name: d2b3ff790fee10ef4150cdb17f1c979e7001a420b2629b153b4dbf4c0b489704
This commit is contained in:
parent
410e244ed6
commit
90fc249052
26 changed files with 1447 additions and 1061 deletions
10
src/Cube.mm
10
src/Cube.mm
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
OF_APPLICATION_DELEGATE(Cube)
|
||||
|
||||
VARF(gamespeed, 10, 100, 1000, if (multiplayer()) gamespeed = 100);
|
||||
|
@ -88,6 +90,8 @@ VARP(minmillis, 0, 5, 1000);
|
|||
if (SDL_Init(SDL_INIT_TIMER | SDL_INIT_VIDEO | par) < 0)
|
||||
fatal(@"Unable to initialize SDL");
|
||||
|
||||
initPlayers();
|
||||
|
||||
log(@"net");
|
||||
if (enet_initialize() < 0)
|
||||
fatal(@"Unable to initialise network module");
|
||||
|
@ -212,7 +216,7 @@ VARP(minmillis, 0, 5, 1000);
|
|||
static float fps = 30.0f;
|
||||
fps = (1000.0f / curtime + fps * 50) / 51;
|
||||
|
||||
computeraytable(player1->o.x, player1->o.y);
|
||||
computeraytable(player1.o.x, player1.o.y);
|
||||
readdepth(_width, _height);
|
||||
SDL_GL_SwapWindow(_window);
|
||||
extern void updatevol();
|
||||
|
@ -221,9 +225,9 @@ VARP(minmillis, 0, 5, 1000);
|
|||
// cheap hack to get rid of initial sparklies, even when triple
|
||||
// buffering etc.
|
||||
if (_framesInMap++ < 5) {
|
||||
player1->yaw += 5;
|
||||
player1.yaw += 5;
|
||||
gl_drawframe(_width, _height, fps);
|
||||
player1->yaw -= 5;
|
||||
player1.yaw -= 5;
|
||||
}
|
||||
|
||||
gl_drawframe(_width, _height, fps);
|
||||
|
|
55
src/DynamicEntity.h
Normal file
55
src/DynamicEntity.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
#import <ObjFW/ObjFW.h>
|
||||
|
||||
// players & monsters
|
||||
@interface DynamicEntity: OFObject <OFCopying>
|
||||
@property (class, readonly, nonatomic) size_t serializedSize;
|
||||
|
||||
// origin, velocity
|
||||
@property (nonatomic) OFVector3D o, vel;
|
||||
// used as OFVector3D in one place
|
||||
@property (nonatomic) float yaw, pitch, roll;
|
||||
// cubes per second, 24 for player
|
||||
@property (nonatomic) float maxspeed;
|
||||
// from his eyes
|
||||
@property (nonatomic) bool outsidemap;
|
||||
@property (nonatomic) bool inwater;
|
||||
@property (nonatomic) bool onfloor, jumpnext;
|
||||
@property (nonatomic) int move, strafe;
|
||||
// see input code
|
||||
@property (nonatomic) bool k_left, k_right, k_up, k_down;
|
||||
// used for fake gravity
|
||||
@property (nonatomic) int timeinair;
|
||||
// bounding box size
|
||||
@property (nonatomic) float radius, eyeheight, aboveeye;
|
||||
@property (nonatomic) int lastupdate, plag, ping;
|
||||
// sequence id for each respawn, used in damage test
|
||||
@property (nonatomic) int lifesequence;
|
||||
// one of CS_* below
|
||||
@property (nonatomic) int state;
|
||||
@property (nonatomic) int frags;
|
||||
@property (nonatomic) int health, armour, armourtype, quadmillis;
|
||||
@property (nonatomic) int gunselect, gunwait;
|
||||
@property (nonatomic) int lastaction, lastattackgun, lastmove;
|
||||
@property (nonatomic) bool attacking;
|
||||
@property (readonly, nonatomic) int *ammo;
|
||||
// one of M_* below, M_NONE means human
|
||||
@property (nonatomic) int monsterstate;
|
||||
// see monster.cpp
|
||||
@property (nonatomic) int mtype;
|
||||
// monster wants to kill this entity
|
||||
@property (nonatomic) DynamicEntity *enemy;
|
||||
// monster wants to look in this direction
|
||||
@property (nonatomic) float targetyaw;
|
||||
// used by physics to signal ai
|
||||
@property (nonatomic) bool blocked, moving;
|
||||
// millis at which transition to another monsterstate takes place
|
||||
@property (nonatomic) int trigger;
|
||||
// delayed attacks
|
||||
@property (nonatomic) OFVector3D attacktarget;
|
||||
// how many times already hit by fellow monster
|
||||
@property (nonatomic) int anger;
|
||||
@property (copy, nonatomic) OFString *name, *team;
|
||||
|
||||
- (OFData *)dataBySerializing;
|
||||
- (void)setFromSerializedData:(OFData *)data;
|
||||
@end
|
235
src/DynamicEntity.mm
Normal file
235
src/DynamicEntity.mm
Normal file
|
@ -0,0 +1,235 @@
|
|||
#import "DynamicEntity.h"
|
||||
|
||||
#include "cube.h"
|
||||
|
||||
struct dynent {
|
||||
OFVector3D o, vel;
|
||||
float yaw, pitch, roll;
|
||||
float maxspeed;
|
||||
bool outsidemap;
|
||||
bool inwater;
|
||||
bool onfloor, jumpnext;
|
||||
int move, strafe;
|
||||
bool k_left, k_right, k_up, k_down;
|
||||
int timeinair;
|
||||
float radius, eyeheight, aboveeye;
|
||||
int lastupdate, plag, ping;
|
||||
int lifesequence;
|
||||
int state;
|
||||
int frags;
|
||||
int health, armour, armourtype, quadmillis;
|
||||
int gunselect, gunwait;
|
||||
int lastaction, lastattackgun, lastmove;
|
||||
bool attacking;
|
||||
int ammo[NUMGUNS];
|
||||
int monsterstate;
|
||||
int mtype;
|
||||
void *enemy;
|
||||
float targetyaw;
|
||||
bool blocked, moving;
|
||||
int trigger;
|
||||
OFVector3D attacktarget;
|
||||
int anger;
|
||||
char name[260], team[260];
|
||||
};
|
||||
|
||||
@implementation DynamicEntity
|
||||
+ (size_t)serializedSize
|
||||
{
|
||||
return sizeof(dynent);
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
|
||||
_ammo = (int *)OFAllocZeroedMemory(NUMGUNS, sizeof(int));
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
OFFreeMemory(_ammo);
|
||||
}
|
||||
|
||||
- (id)copy
|
||||
{
|
||||
DynamicEntity *copy = [[self.class alloc] init];
|
||||
|
||||
copy->_o = _o;
|
||||
copy->_vel = _vel;
|
||||
copy->_yaw = _yaw;
|
||||
copy->_pitch = _pitch;
|
||||
copy->_roll = _roll;
|
||||
copy->_maxspeed = _maxspeed;
|
||||
copy->_outsidemap = _outsidemap;
|
||||
copy->_inwater = _inwater;
|
||||
copy->_onfloor = _onfloor;
|
||||
copy->_jumpnext = _jumpnext;
|
||||
copy->_move = _move;
|
||||
copy->_strafe = _strafe;
|
||||
copy->_k_left = _k_left;
|
||||
copy->_k_right = _k_right;
|
||||
copy->_k_up = _k_up;
|
||||
copy->_k_down = _k_down;
|
||||
copy->_timeinair = _timeinair;
|
||||
copy->_radius = _radius;
|
||||
copy->_eyeheight = _eyeheight;
|
||||
copy->_aboveeye = _aboveeye;
|
||||
copy->_lastupdate = _lastupdate;
|
||||
copy->_plag = _plag;
|
||||
copy->_ping = _ping;
|
||||
copy->_lifesequence = _lifesequence;
|
||||
copy->_state = _state;
|
||||
copy->_frags = _frags;
|
||||
copy->_health = _health;
|
||||
copy->_armour = _armour;
|
||||
copy->_armourtype = _armourtype;
|
||||
copy->_quadmillis = _quadmillis;
|
||||
copy->_gunselect = _gunselect;
|
||||
copy->_gunwait = _gunwait;
|
||||
copy->_lastaction = _lastaction;
|
||||
copy->_lastattackgun = _lastattackgun;
|
||||
copy->_lastmove = _lastmove;
|
||||
copy->_attacking = _attacking;
|
||||
|
||||
for (size_t i = 0; i < NUMGUNS; i++)
|
||||
copy->_ammo[i] = _ammo[i];
|
||||
|
||||
copy->_monsterstate = _monsterstate;
|
||||
copy->_mtype = _mtype;
|
||||
copy->_enemy = _enemy;
|
||||
copy->_targetyaw = _targetyaw;
|
||||
copy->_blocked = _blocked;
|
||||
copy->_moving = _moving;
|
||||
copy->_trigger = _trigger;
|
||||
copy->_attacktarget = _attacktarget;
|
||||
copy->_anger = _anger;
|
||||
|
||||
copy->_name = [_name copy];
|
||||
copy->_team = [_team copy];
|
||||
|
||||
return copy;
|
||||
}
|
||||
|
||||
- (OFData *)dataBySerializing
|
||||
{
|
||||
// This is frighteningly *TERRIBLE*, but the format used by existing
|
||||
// savegames.
|
||||
dynent data = { .o = _o,
|
||||
.vel = _vel,
|
||||
.yaw = _yaw,
|
||||
.pitch = _pitch,
|
||||
.roll = _roll,
|
||||
.maxspeed = _maxspeed,
|
||||
.outsidemap = _outsidemap,
|
||||
.inwater = _inwater,
|
||||
.onfloor = _onfloor,
|
||||
.jumpnext = _jumpnext,
|
||||
.move = _move,
|
||||
.strafe = _strafe,
|
||||
.k_left = _k_left,
|
||||
.k_right = _k_right,
|
||||
.k_up = _k_up,
|
||||
.k_down = _k_down,
|
||||
.timeinair = _timeinair,
|
||||
.radius = _radius,
|
||||
.eyeheight = _eyeheight,
|
||||
.aboveeye = _aboveeye,
|
||||
.lastupdate = _lastupdate,
|
||||
.plag = _plag,
|
||||
.ping = _ping,
|
||||
.lifesequence = _lifesequence,
|
||||
.state = _state,
|
||||
.frags = _frags,
|
||||
.health = _health,
|
||||
.armour = _armour,
|
||||
.armourtype = _armourtype,
|
||||
.quadmillis = _quadmillis,
|
||||
.gunselect = _gunselect,
|
||||
.gunwait = _gunwait,
|
||||
.lastaction = _lastaction,
|
||||
.lastattackgun = _lastattackgun,
|
||||
.lastmove = _lastmove,
|
||||
.attacking = _attacking,
|
||||
.monsterstate = _monsterstate,
|
||||
.mtype = _mtype,
|
||||
.targetyaw = _targetyaw,
|
||||
.blocked = _blocked,
|
||||
.moving = _moving,
|
||||
.trigger = _trigger,
|
||||
.attacktarget = _attacktarget,
|
||||
.anger = _anger };
|
||||
|
||||
for (int i = 0; i < NUMGUNS; i++)
|
||||
data.ammo[i] = _ammo[i];
|
||||
|
||||
memcpy(data.name, _name.UTF8String, min(_name.UTF8StringLength, 259));
|
||||
memcpy(data.team, _team.UTF8String, min(_team.UTF8StringLength, 259));
|
||||
|
||||
return [OFData dataWithItems:&data count:sizeof(data)];
|
||||
}
|
||||
|
||||
- (void)setFromSerializedData:(OFData *)data
|
||||
{
|
||||
struct dynent d;
|
||||
|
||||
if (data.count != sizeof(dynent))
|
||||
@throw [OFOutOfRangeException exception];
|
||||
|
||||
memcpy(&d, data.items, data.count);
|
||||
|
||||
_o = d.o;
|
||||
_vel = d.vel;
|
||||
_yaw = d.yaw;
|
||||
_pitch = d.pitch;
|
||||
_roll = d.roll;
|
||||
_maxspeed = d.maxspeed;
|
||||
_outsidemap = d.outsidemap;
|
||||
_inwater = d.inwater;
|
||||
_onfloor = d.onfloor;
|
||||
_jumpnext = d.jumpnext;
|
||||
_move = d.move;
|
||||
_strafe = d.strafe;
|
||||
_k_left = d.k_left;
|
||||
_k_right = d.k_right;
|
||||
_k_up = d.k_up;
|
||||
_k_down = d.k_down;
|
||||
_timeinair = d.timeinair;
|
||||
_radius = d.radius;
|
||||
_eyeheight = d.eyeheight;
|
||||
_aboveeye = d.aboveeye;
|
||||
_lastupdate = d.lastupdate;
|
||||
_plag = d.plag;
|
||||
_ping = d.ping;
|
||||
_lifesequence = d.lifesequence;
|
||||
_state = d.state;
|
||||
_frags = d.frags;
|
||||
_health = d.health;
|
||||
_armour = d.armour;
|
||||
_armourtype = d.armourtype;
|
||||
_quadmillis = d.quadmillis;
|
||||
_gunselect = d.gunselect;
|
||||
_gunwait = d.gunwait;
|
||||
_lastaction = d.lastaction;
|
||||
_lastattackgun = d.lastattackgun;
|
||||
_lastmove = d.lastmove;
|
||||
_attacking = d.attacking;
|
||||
|
||||
for (int i = 0; i < NUMGUNS; i++)
|
||||
_ammo[i] = d.ammo[i];
|
||||
|
||||
_monsterstate = d.monsterstate;
|
||||
_mtype = d.mtype;
|
||||
_targetyaw = d.targetyaw;
|
||||
_blocked = d.blocked;
|
||||
_moving = d.moving;
|
||||
_trigger = d.trigger;
|
||||
_attacktarget = d.attacktarget;
|
||||
_anger = d.anger;
|
||||
|
||||
_name = [[OFString alloc] initWithUTF8String:d.name];
|
||||
_team = [[OFString alloc] initWithUTF8String:d.team];
|
||||
}
|
||||
@end
|
|
@ -1,11 +1,11 @@
|
|||
#import <ObjFW/ObjFW.h>
|
||||
|
||||
typedef struct dynent dynent;
|
||||
@class DynamicEntity;
|
||||
|
||||
@interface Projectile: OFObject
|
||||
@property (nonatomic) OFVector3D o, to;
|
||||
@property (nonatomic) float speed;
|
||||
@property (nonatomic) dynent *owner;
|
||||
@property (nonatomic) DynamicEntity *owner;
|
||||
@property (nonatomic) int gun;
|
||||
@property (nonatomic) bool inuse, local;
|
||||
@end
|
||||
|
|
|
@ -2,12 +2,16 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
ENetHost *clienthost = NULL;
|
||||
int connecting = 0;
|
||||
int connattempts = 0;
|
||||
int disconnecting = 0;
|
||||
int clientnum = -1; // our client id in the game
|
||||
bool c2sinit = false; // whether we need to tell the other clients our stats
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
static ENetHost *clienthost = NULL;
|
||||
static int connecting = 0;
|
||||
static int connattempts = 0;
|
||||
static int disconnecting = 0;
|
||||
// our client id in the game
|
||||
int clientnum = -1;
|
||||
// whether we need to tell the other clients our stats
|
||||
bool c2sinit = false;
|
||||
|
||||
int
|
||||
getclientnum()
|
||||
|
@ -59,9 +63,13 @@ throttle()
|
|||
void
|
||||
newname(OFString *name)
|
||||
{
|
||||
c2sinit = false;
|
||||
@autoreleasepool {
|
||||
strn0cpy(player1->name, name.UTF8String, 16);
|
||||
c2sinit = false;
|
||||
|
||||
if (name.length > 16)
|
||||
name = [name substringToIndex:16];
|
||||
|
||||
player1.name = name;
|
||||
}
|
||||
}
|
||||
COMMANDN(name, newname, ARG_1STR)
|
||||
|
@ -69,9 +77,13 @@ COMMANDN(name, newname, ARG_1STR)
|
|||
void
|
||||
newteam(OFString *name)
|
||||
{
|
||||
c2sinit = false;
|
||||
@autoreleasepool {
|
||||
strn0cpy(player1->team, name.UTF8String, 5);
|
||||
c2sinit = false;
|
||||
|
||||
if (name.length > 5)
|
||||
name = [name substringToIndex:5];
|
||||
|
||||
player1.team = name;
|
||||
}
|
||||
}
|
||||
COMMANDN(team, newteam, ARG_1STR)
|
||||
|
@ -79,8 +91,8 @@ COMMANDN(team, newteam, ARG_1STR)
|
|||
void
|
||||
writeclientinfo(OFStream *stream)
|
||||
{
|
||||
[stream writeFormat:@"name \"%s\"\nteam \"%s\"\n", player1->name,
|
||||
player1->team];
|
||||
[stream writeFormat:@"name \"%@\"\nteam \"%@\"\n", player1.name,
|
||||
player1.team];
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -137,8 +149,8 @@ disconnect(int onlyclean, int async)
|
|||
disconnecting = 0;
|
||||
clientnum = -1;
|
||||
c2sinit = false;
|
||||
player1->lifesequence = 0;
|
||||
loopv(players) zapdynent(players[i]);
|
||||
player1.lifesequence = 0;
|
||||
[players removeAllObjects];
|
||||
|
||||
localdisconnect();
|
||||
|
||||
|
@ -168,7 +180,7 @@ static OFString *ctext;
|
|||
void
|
||||
toserver(OFString *text)
|
||||
{
|
||||
conoutf(@"%s:\f %@", player1->name, text);
|
||||
conoutf(@"%@:\f %@", player1.name, text);
|
||||
ctext = text;
|
||||
}
|
||||
|
||||
|
@ -271,8 +283,9 @@ sendpackettoserv(void *packet)
|
|||
localclienttoserver((ENetPacket *)packet);
|
||||
}
|
||||
|
||||
// send update to the server
|
||||
void
|
||||
c2sinfo(dynent *d) // send update to the server
|
||||
c2sinfo(DynamicEntity *d)
|
||||
{
|
||||
@autoreleasepool {
|
||||
if (clientnum < 0)
|
||||
|
@ -295,26 +308,24 @@ c2sinfo(dynent *d) // send update to the server
|
|||
} else {
|
||||
putint(p, SV_POS);
|
||||
putint(p, clientnum);
|
||||
putint(p,
|
||||
(int)(d->o.x *
|
||||
DMF)); // quantize coordinates to 1/16th
|
||||
// of a cube, between 1 and 3 bytes
|
||||
putint(p, (int)(d->o.y * DMF));
|
||||
putint(p, (int)(d->o.z * DMF));
|
||||
putint(p, (int)(d->yaw * DAF));
|
||||
putint(p, (int)(d->pitch * DAF));
|
||||
putint(p, (int)(d->roll * DAF));
|
||||
putint(
|
||||
p, (int)(d->vel.x * DVF)); // quantize to 1/100,
|
||||
// almost always 1 byte
|
||||
putint(p, (int)(d->vel.y * DVF));
|
||||
putint(p, (int)(d->vel.z * DVF));
|
||||
// quantize coordinates to 1/16th of a cube, between 1
|
||||
// and 3 bytes
|
||||
putint(p, (int)(d.o.x * DMF));
|
||||
putint(p, (int)(d.o.y * DMF));
|
||||
putint(p, (int)(d.o.z * DMF));
|
||||
putint(p, (int)(d.yaw * DAF));
|
||||
putint(p, (int)(d.pitch * DAF));
|
||||
putint(p, (int)(d.roll * DAF));
|
||||
// quantize to 1/100, almost always 1 byte
|
||||
putint(p, (int)(d.vel.x * DVF));
|
||||
putint(p, (int)(d.vel.y * DVF));
|
||||
putint(p, (int)(d.vel.z * DVF));
|
||||
// pack rest in 1 byte: strafe:2, move:2, onfloor:1,
|
||||
// state:3
|
||||
putint(p,
|
||||
(d->strafe & 3) | ((d->move & 3) << 2) |
|
||||
(((int)d->onfloor) << 4) |
|
||||
((editmode ? CS_EDITING : d->state) << 5));
|
||||
(d.strafe & 3) | ((d.move & 3) << 2) |
|
||||
(((int)d.onfloor) << 4) |
|
||||
((editmode ? CS_EDITING : d.state) << 5));
|
||||
|
||||
if (senditemstoserver) {
|
||||
packet->flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
|
@ -332,14 +343,14 @@ c2sinfo(dynent *d) // send update to the server
|
|||
sendstring(ctext, p);
|
||||
ctext = @"";
|
||||
}
|
||||
if (!c2sinit) // tell other clients who I am
|
||||
{
|
||||
// tell other clients who I am
|
||||
if (!c2sinit) {
|
||||
packet->flags = ENET_PACKET_FLAG_RELIABLE;
|
||||
c2sinit = true;
|
||||
putint(p, SV_INITC2S);
|
||||
sendstring(@(player1->name), p);
|
||||
sendstring(@(player1->team), p);
|
||||
putint(p, player1->lifesequence);
|
||||
sendstring(player1.name, p);
|
||||
sendstring(player1.team, p);
|
||||
putint(p, player1.lifesequence);
|
||||
}
|
||||
for (OFData *msg in messages) {
|
||||
// send messages collected during the previous
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
// render players & monsters
|
||||
// very messy ad-hoc handling of animation frames, should be made more
|
||||
// configurable
|
||||
|
@ -14,13 +16,14 @@ int range[] = { 6, 6, 8, 28, 1, 1, 1, 1, 8, 19, 4, 18, 40, 1, 6, 15, 1, 1, 1,
|
|||
1 };
|
||||
|
||||
void
|
||||
renderclient(dynent *d, bool team, OFString *mdlname, bool hellpig, float scale)
|
||||
renderclient(
|
||||
DynamicEntity *d, bool team, OFString *mdlname, bool hellpig, float scale)
|
||||
{
|
||||
int n = 3;
|
||||
float speed = 100.0f;
|
||||
float mz = d->o.z - d->eyeheight + 1.55f * scale;
|
||||
float mz = d.o.z - d.eyeheight + 1.55f * scale;
|
||||
int basetime = -((intptr_t)d & 0xFFF);
|
||||
if (d->state == CS_DEAD) {
|
||||
if (d.state == CS_DEAD) {
|
||||
int r;
|
||||
if (hellpig) {
|
||||
n = 2;
|
||||
|
@ -29,8 +32,8 @@ renderclient(dynent *d, bool team, OFString *mdlname, bool hellpig, float scale)
|
|||
n = (intptr_t)d % 3;
|
||||
r = range[n];
|
||||
}
|
||||
basetime = d->lastaction;
|
||||
int t = lastmillis - d->lastaction;
|
||||
basetime = d.lastaction;
|
||||
int t = lastmillis - d.lastaction;
|
||||
if (t < 0 || t > 20000)
|
||||
return;
|
||||
if (t > (r - 1) * 100) {
|
||||
|
@ -43,33 +46,33 @@ renderclient(dynent *d, bool team, OFString *mdlname, bool hellpig, float scale)
|
|||
if (mz < -1000)
|
||||
return;
|
||||
// mdl = (((int)d>>6)&1)+1;
|
||||
// mz = d->o.z-d->eyeheight+0.2f;
|
||||
// mz = d.o.z-d.eyeheight+0.2f;
|
||||
// scale = 1.2f;
|
||||
} else if (d->state == CS_EDITING) {
|
||||
} else if (d.state == CS_EDITING) {
|
||||
n = 16;
|
||||
} else if (d->state == CS_LAGGED) {
|
||||
} else if (d.state == CS_LAGGED) {
|
||||
n = 17;
|
||||
} else if (d->monsterstate == M_ATTACKING) {
|
||||
} else if (d.monsterstate == M_ATTACKING) {
|
||||
n = 8;
|
||||
} else if (d->monsterstate == M_PAIN) {
|
||||
} else if (d.monsterstate == M_PAIN) {
|
||||
n = 10;
|
||||
} else if ((!d->move && !d->strafe) || !d->moving) {
|
||||
} else if ((!d.move && !d.strafe) || !d.moving) {
|
||||
n = 12;
|
||||
} else if (!d->onfloor && d->timeinair > 100) {
|
||||
} else if (!d.onfloor && d.timeinair > 100) {
|
||||
n = 18;
|
||||
} else {
|
||||
n = 14;
|
||||
speed = 1200 / d->maxspeed * scale;
|
||||
speed = 1200 / d.maxspeed * scale;
|
||||
if (hellpig)
|
||||
speed = 300 / d->maxspeed;
|
||||
speed = 300 / d.maxspeed;
|
||||
}
|
||||
if (hellpig) {
|
||||
n++;
|
||||
scale *= 32;
|
||||
mz -= 1.9f;
|
||||
}
|
||||
rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d->o.x, mz, d->o.y,
|
||||
d->yaw + 90, d->pitch / 2, team, scale, speed, 0, basetime);
|
||||
rendermodel(mdlname, frame[n], range[n], 0, 1.5f, d.o.x, mz, d.o.y,
|
||||
d.yaw + 90, d.pitch / 2, team, scale, speed, 0, basetime);
|
||||
}
|
||||
|
||||
extern int democlientnum;
|
||||
|
@ -77,10 +80,15 @@ extern int democlientnum;
|
|||
void
|
||||
renderclients()
|
||||
{
|
||||
dynent *d;
|
||||
loopv(players) if ((d = players[i]) &&
|
||||
(!demoplayback || i != democlientnum)) renderclient(d,
|
||||
isteam(player1->team, d->team), @"monster/ogro", false, 1.0f);
|
||||
size_t i = 0;
|
||||
for (id player in players) {
|
||||
if (player != [OFNull null] &&
|
||||
(!demoplayback || i != democlientnum))
|
||||
renderclient(player,
|
||||
isteam(player1.team, [player team]),
|
||||
@"monster/ogro", false, 1.0f);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
// creation of scoreboard pseudo-menu
|
||||
|
@ -97,15 +105,15 @@ showscores(bool on)
|
|||
static OFMutableArray<OFString *> *scoreLines;
|
||||
|
||||
void
|
||||
renderscore(dynent *d)
|
||||
renderscore(DynamicEntity *d)
|
||||
{
|
||||
@autoreleasepool {
|
||||
OFString *lag = [OFString stringWithFormat:@"%d", d->plag];
|
||||
OFString *name = [OFString stringWithFormat:@"(%s)", d->name];
|
||||
OFString *line = [OFString
|
||||
stringWithFormat:@"%d\t%@\t%d\t%s\t%@", d->frags,
|
||||
(d->state == CS_LAGGED ? @"LAG" : lag), d->ping, d->team,
|
||||
(d->state == CS_DEAD ? name : @(d->name))];
|
||||
OFString *lag = [OFString stringWithFormat:@"%d", d.plag];
|
||||
OFString *name = [OFString stringWithFormat:@"(%@)", d.name];
|
||||
OFString *line =
|
||||
[OFString stringWithFormat:@"%d\t%@\t%d\t%@\t%@", d.frags,
|
||||
(d.state == CS_LAGGED ? @"LAG" : lag), d.ping,
|
||||
d.team, (d.state == CS_DEAD ? name : d.name)];
|
||||
|
||||
if (scoreLines == nil)
|
||||
scoreLines = [[OFMutableArray alloc] init];
|
||||
|
@ -118,21 +126,16 @@ renderscore(dynent *d)
|
|||
|
||||
static const int maxTeams = 4;
|
||||
static OFString *teamName[maxTeams];
|
||||
static int teamScore[maxTeams], teamsUsed;
|
||||
static int teamScore[maxTeams];
|
||||
static size_t teamsUsed;
|
||||
|
||||
void
|
||||
addteamscore(dynent *d)
|
||||
addteamscore(DynamicEntity *d)
|
||||
{
|
||||
if (d == NULL)
|
||||
return;
|
||||
|
||||
@autoreleasepool {
|
||||
OFString *team = @(d->team);
|
||||
|
||||
loopi(teamsUsed)
|
||||
{
|
||||
if ([teamName[i] isEqual:team]) {
|
||||
teamScore[i] += d->frags;
|
||||
for (size_t i = 0; i < teamsUsed; i++) {
|
||||
if ([teamName[i] isEqual:d.team]) {
|
||||
teamScore[i] += d.frags;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -140,8 +143,8 @@ addteamscore(dynent *d)
|
|||
if (teamsUsed == maxTeams)
|
||||
return;
|
||||
|
||||
teamName[teamsUsed] = @(d->team);
|
||||
teamScore[teamsUsed++] = d->frags;
|
||||
teamName[teamsUsed] = d.team;
|
||||
teamScore[teamsUsed++] = d.frags;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -153,19 +156,21 @@ renderscores()
|
|||
[scoreLines removeAllObjects];
|
||||
if (!demoplayback)
|
||||
renderscore(player1);
|
||||
loopv(players) if (players[i]) renderscore(players[i]);
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
renderscore(player);
|
||||
sortmenu();
|
||||
if (m_teammode) {
|
||||
teamsUsed = 0;
|
||||
loopv(players) addteamscore(players[i]);
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
addteamscore(player);
|
||||
if (!demoplayback)
|
||||
addteamscore(player1);
|
||||
OFMutableString *teamScores = [[OFMutableString alloc] init];
|
||||
loopj(teamsUsed)
|
||||
{
|
||||
for (size_t j = 0; j < teamsUsed; j++)
|
||||
[teamScores appendFormat:@"[ %@: %d ]", teamName[j],
|
||||
teamScore[j]];
|
||||
}
|
||||
menumanual(0, scoreLines.count, @"");
|
||||
@autoreleasepool {
|
||||
menumanual(0, scoreLines.count + 1, teamScores);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
int nextmode = 0; // nextmode becomes gamemode after next map load
|
||||
VAR(gamemode, 1, 0, 0);
|
||||
|
||||
|
@ -14,8 +16,15 @@ COMMAND(mode, ARG_1INT)
|
|||
|
||||
bool intermission = false;
|
||||
|
||||
dynent *player1 = newdynent(); // our client
|
||||
dvector players; // other clients
|
||||
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);
|
||||
|
@ -32,49 +41,51 @@ getclientmap()
|
|||
}
|
||||
|
||||
void
|
||||
resetmovement(dynent *d)
|
||||
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;
|
||||
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(dynent *d) // reset player state not persistent accross spawns
|
||||
spawnstate(DynamicEntity *d)
|
||||
{
|
||||
resetmovement(d);
|
||||
d->vel.x = d->vel.y = d->vel.z = 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;
|
||||
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;
|
||||
d.gunselect = GUN_RIFLE;
|
||||
d.armour = 0;
|
||||
if (m_noitemsrail) {
|
||||
d->health = 1;
|
||||
d->ammo[GUN_RIFLE] = 100;
|
||||
d.health = 1;
|
||||
d.ammo[GUN_RIFLE] = 100;
|
||||
} else {
|
||||
if (gamemode == 12) {
|
||||
d->gunselect = GUN_FIST;
|
||||
// eihrul's secret "instafist" mode
|
||||
d.gunselect = GUN_FIST;
|
||||
return;
|
||||
} // eihrul's secret "instafist" mode
|
||||
d->health = 256;
|
||||
}
|
||||
d.health = 256;
|
||||
if (m_tarena) {
|
||||
int gun1 = rnd(4) + 1;
|
||||
baseammo(d->gunselect = gun1);
|
||||
baseammo(d.gunselect = gun1);
|
||||
for (;;) {
|
||||
int gun2 = rnd(4) + 1;
|
||||
if (gun1 != gun2) {
|
||||
|
@ -82,47 +93,44 @@ spawnstate(dynent *d) // reset player state not persistent accross spawns
|
|||
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 if (m_arena) {
|
||||
// insta arena
|
||||
d.ammo[GUN_RIFLE] = 100;
|
||||
} else {
|
||||
d->ammo[GUN_SG] = 5;
|
||||
// efficiency
|
||||
loopi(4) baseammo(i + 1);
|
||||
d.gunselect = GUN_CG;
|
||||
}
|
||||
d.ammo[GUN_CG] /= 2;
|
||||
}
|
||||
} else
|
||||
d.ammo[GUN_SG] = 5;
|
||||
}
|
||||
|
||||
dynent *
|
||||
DynamicEntity *
|
||||
newdynent() // create a new blank player or monster
|
||||
{
|
||||
dynent *d = (dynent *)OFAllocMemory(1, sizeof(dynent));
|
||||
d->o.x = 0;
|
||||
d->o.y = 0;
|
||||
d->o.z = 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[0] = d->team[0] = 0;
|
||||
d->blocked = false;
|
||||
d->lifesequence = 0;
|
||||
d->state = CS_ALIVE;
|
||||
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;
|
||||
}
|
||||
|
@ -135,16 +143,16 @@ respawnself()
|
|||
}
|
||||
|
||||
void
|
||||
arenacount(dynent *d, int &alive, int &dead, char *&lastteam, bool &oneteam)
|
||||
arenacount(
|
||||
DynamicEntity *d, int &alive, int &dead, OFString **lastteam, bool &oneteam)
|
||||
{
|
||||
if (d->state != CS_DEAD) {
|
||||
if (d.state != CS_DEAD) {
|
||||
alive++;
|
||||
if (lastteam && strcmp(lastteam, d->team))
|
||||
if (![*lastteam isEqual:d.team])
|
||||
oneteam = false;
|
||||
lastteam = d->team;
|
||||
} else {
|
||||
*lastteam = d.team;
|
||||
} else
|
||||
dead++;
|
||||
}
|
||||
}
|
||||
|
||||
int arenarespawnwait = 0;
|
||||
|
@ -162,11 +170,13 @@ arenarespawn()
|
|||
} else if (arenadetectwait == 0 || arenadetectwait < lastmillis) {
|
||||
arenadetectwait = 0;
|
||||
int alive = 0, dead = 0;
|
||||
char *lastteam = NULL;
|
||||
OFString *lastteam = nil;
|
||||
bool oneteam = true;
|
||||
loopv(players) if (players[i])
|
||||
arenacount(players[i], alive, dead, lastteam, oneteam);
|
||||
arenacount(player1, alive, dead, lastteam, oneteam);
|
||||
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...");
|
||||
|
@ -177,43 +187,39 @@ arenarespawn()
|
|||
conoutf(@"everyone died!");
|
||||
arenarespawnwait = lastmillis + 5000;
|
||||
arenadetectwait = lastmillis + 10000;
|
||||
player1->roll = 0;
|
||||
player1.roll = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
zapdynent(dynent *&d)
|
||||
{
|
||||
OFFreeMemory(d);
|
||||
d = NULL;
|
||||
}
|
||||
|
||||
extern int democlientnum;
|
||||
|
||||
void
|
||||
otherplayers()
|
||||
{
|
||||
loopv(players) if (players[i])
|
||||
{
|
||||
const int lagtime = lastmillis - players[i]->lastupdate;
|
||||
if (lagtime > 1000 && players[i]->state == CS_ALIVE) {
|
||||
players[i]->state = CS_LAGGED;
|
||||
size_t i = 0;
|
||||
for (id player in players) {
|
||||
if (player != [OFNull null]) {
|
||||
const int lagtime = lastmillis - [player lastupdate];
|
||||
if (lagtime > 1000 && [player state] == CS_ALIVE) {
|
||||
[player setState:CS_LAGGED];
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (lagtime && players[i]->state != CS_DEAD &&
|
||||
if (lagtime && [player state] != CS_DEAD &&
|
||||
(!demoplayback || i != democlientnum))
|
||||
moveplayer(
|
||||
players[i], 2, false); // use physics to extrapolate
|
||||
// player position
|
||||
// use physics to extrapolate player position
|
||||
moveplayer(player, 2, false);
|
||||
}
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
respawn()
|
||||
{
|
||||
if (player1->state == CS_DEAD) {
|
||||
player1->attacking = false;
|
||||
if (player1.state == CS_DEAD) {
|
||||
player1.attacking = false;
|
||||
if (m_arena) {
|
||||
conoutf(@"waiting for new round to start...");
|
||||
return;
|
||||
|
@ -262,19 +268,19 @@ updateworld(int millis) // main game update loop
|
|||
otherplayers();
|
||||
if (!demoplayback) {
|
||||
monsterthink();
|
||||
if (player1->state == CS_DEAD) {
|
||||
if (lastmillis - player1->lastaction < 2000) {
|
||||
player1->move = player1->strafe = 0;
|
||||
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)
|
||||
lastmillis - player1.lastaction > 10000)
|
||||
respawn();
|
||||
} else if (!intermission) {
|
||||
moveplayer(player1, 20, true);
|
||||
checkitems();
|
||||
}
|
||||
c2sinfo(player1); // do this last, to reduce the
|
||||
// effective frame lag
|
||||
// do this last, to reduce the effective frame lag
|
||||
c2sinfo(player1);
|
||||
}
|
||||
}
|
||||
lastmillis = millis;
|
||||
|
@ -282,46 +288,43 @@ updateworld(int millis) // main game update loop
|
|||
|
||||
// brute force but effective way to find a free spawn spot in the map
|
||||
void
|
||||
entinmap(dynent *d)
|
||||
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;
|
||||
d->o.x += dx;
|
||||
d->o.y += dy;
|
||||
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.x -= dx;
|
||||
d->o.y -= dy;
|
||||
d.o = old;
|
||||
}
|
||||
conoutf(@"can't find entity spawn spot! (%d, %d)", (int)d->o.x,
|
||||
(int)d->o.y);
|
||||
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(dynent *d) // place at random spawn. also used by monsters!
|
||||
spawnplayer(DynamicEntity *d)
|
||||
{
|
||||
int r = fixspawn-- > 0 ? 4 : rnd(10) + 1;
|
||||
loopi(r) spawncycle = findentity(PLAYERSTART, spawncycle + 1);
|
||||
if (spawncycle != -1) {
|
||||
d->o.x = ents[spawncycle].x;
|
||||
d->o.y = ents[spawncycle].y;
|
||||
d->o.z = ents[spawncycle].z;
|
||||
d->yaw = ents[spawncycle].attr1;
|
||||
d->pitch = 0;
|
||||
d->roll = 0;
|
||||
} else {
|
||||
d->o.x = d->o.y = (float)ssize / 2;
|
||||
d->o.z = 4;
|
||||
}
|
||||
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;
|
||||
d.state = CS_ALIVE;
|
||||
}
|
||||
|
||||
// movement input code
|
||||
|
@ -329,9 +332,9 @@ spawnplayer(dynent *d) // place at random spawn. also used by monsters!
|
|||
#define dir(name, v, d, s, os) \
|
||||
void name(bool isdown) \
|
||||
{ \
|
||||
player1->s = isdown; \
|
||||
player1->v = isdown ? d : (player1->os ? -(d) : 0); \
|
||||
player1->lastmove = lastmillis; \
|
||||
player1.s = isdown; \
|
||||
player1.v = isdown ? d : (player1.os ? -(d) : 0); \
|
||||
player1.lastmove = lastmillis; \
|
||||
}
|
||||
|
||||
dir(backward, move, -1, k_down, k_up);
|
||||
|
@ -346,14 +349,14 @@ attack(bool on)
|
|||
return;
|
||||
if (editmode)
|
||||
editdrag(on);
|
||||
else if (player1->attacking = on)
|
||||
else if ((player1.attacking = on))
|
||||
respawn();
|
||||
}
|
||||
|
||||
void
|
||||
jumpn(bool on)
|
||||
{
|
||||
if (!intermission && (player1->jumpnext = on))
|
||||
if (!intermission && (player1.jumpnext = on))
|
||||
respawn();
|
||||
}
|
||||
|
||||
|
@ -369,24 +372,24 @@ 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;
|
||||
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)
|
||||
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) *
|
||||
player1.yaw += (dx / SENSF) * (sensitivity / (float)sensitivityscale);
|
||||
player1.pitch -= (dy / SENSF) *
|
||||
(sensitivity / (float)sensitivityscale) * (invmouse ? -1 : 1);
|
||||
fixplayer1range();
|
||||
}
|
||||
|
@ -394,59 +397,57 @@ mousemove(int dx, int dy)
|
|||
// damage arriving from the network, monsters, yourself, all ends up here.
|
||||
|
||||
void
|
||||
selfdamage(int damage, int actor, dynent *act)
|
||||
selfdamage(int damage, int actor, DynamicEntity *act)
|
||||
{
|
||||
if (player1->state != CS_ALIVE || editmode || intermission)
|
||||
if (player1.state != CS_ALIVE || editmode || intermission)
|
||||
return;
|
||||
damageblend(damage);
|
||||
demoblend(damage);
|
||||
int ad = damage * (player1->armourtype + 1) * 20 /
|
||||
100; // let armour absorb when possible
|
||||
if (ad > player1->armour)
|
||||
ad = player1->armour;
|
||||
player1->armour -= ad;
|
||||
// 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
|
||||
player1.roll += player1.roll > 0
|
||||
? droll
|
||||
: (player1->roll < 0
|
||||
: (player1.roll < 0
|
||||
? -droll
|
||||
: (rnd(2) ? droll
|
||||
: -droll)); // give player a kick depending
|
||||
// on amount of damage
|
||||
if ((player1->health -= damage) <= 0) {
|
||||
if ((player1.health -= damage) <= 0) {
|
||||
if (actor == -2) {
|
||||
conoutf(@"you got killed by %s!", act->name);
|
||||
conoutf(@"you got killed by %@!", act.name);
|
||||
} else if (actor == -1) {
|
||||
actor = getclientnum();
|
||||
conoutf(@"you suicided!");
|
||||
addmsg(1, 2, SV_FRAGS, --player1->frags);
|
||||
addmsg(1, 2, SV_FRAGS, --player1.frags);
|
||||
} else {
|
||||
dynent *a = getclient(actor);
|
||||
if (a) {
|
||||
if (isteam(a->team, player1->team)) {
|
||||
DynamicEntity *a = getclient(actor);
|
||||
if (a != nil) {
|
||||
if (isteam(a.team, player1.team))
|
||||
conoutf(@"you got fragged by a "
|
||||
@"teammate (%s)",
|
||||
a->name);
|
||||
} else {
|
||||
@"teammate (%@)",
|
||||
a.name);
|
||||
else
|
||||
conoutf(
|
||||
@"you got fragged by %s", a->name);
|
||||
}
|
||||
@"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;
|
||||
player1.lifesequence++;
|
||||
player1.attacking = false;
|
||||
player1.state = CS_DEAD;
|
||||
player1.pitch = 0;
|
||||
player1.roll = 60;
|
||||
playsound(S_DIE1 + rnd(2));
|
||||
spawnstate(player1);
|
||||
player1->lastaction = lastmillis;
|
||||
} else {
|
||||
player1.lastaction = lastmillis;
|
||||
} else
|
||||
playsound(S_PAIN6);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -454,7 +455,7 @@ timeupdate(int timeremain)
|
|||
{
|
||||
if (!timeremain) {
|
||||
intermission = true;
|
||||
player1->attacking = false;
|
||||
player1.attacking = false;
|
||||
conoutf(@"intermission:");
|
||||
conoutf(@"game has ended!");
|
||||
showscores(true);
|
||||
|
@ -463,16 +464,31 @@ timeupdate(int timeremain)
|
|||
}
|
||||
}
|
||||
|
||||
dynent *
|
||||
DynamicEntity *
|
||||
getclient(int cn) // ensure valid entity
|
||||
{
|
||||
if (cn < 0 || cn >= MAXCLIENTS) {
|
||||
neterr(@"clientnum");
|
||||
return NULL;
|
||||
return nil;
|
||||
}
|
||||
while (cn >= players.length())
|
||||
players.add(NULL);
|
||||
return players[cn] ? players[cn] : (players[cn] = newdynent());
|
||||
if (players == nil)
|
||||
players = [[OFMutableArray alloc] init];
|
||||
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");
|
||||
if (players == nil)
|
||||
players = [[OFMutableArray alloc] init];
|
||||
while (cn >= players.count)
|
||||
[players addObject:[OFNull null]];
|
||||
players[cn] = client;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -494,8 +510,10 @@ startmap(OFString *name) // called just after a map load
|
|||
projreset();
|
||||
spawncycle = -1;
|
||||
spawnplayer(player1);
|
||||
player1->frags = 0;
|
||||
loopv(players) if (players[i]) players[i]->frags = 0;
|
||||
player1.frags = 0;
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
[player setFrags:0];
|
||||
resetspawns();
|
||||
clientmap = name;
|
||||
if (editmode)
|
||||
|
|
164
src/clients2c.mm
164
src/clients2c.mm
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
extern int clientnum;
|
||||
extern bool c2sinit, senditemstoserver;
|
||||
extern OFString *toservermap;
|
||||
|
@ -32,31 +34,35 @@ changemap(OFString *name) // request map change, server may ignore
|
|||
// just don't overlap with our client
|
||||
|
||||
void
|
||||
updatepos(dynent *d)
|
||||
updatepos(DynamicEntity *d)
|
||||
{
|
||||
const float r = player1->radius + d->radius;
|
||||
const float dx = player1->o.x - d->o.x;
|
||||
const float dy = player1->o.y - d->o.y;
|
||||
const float dz = player1->o.z - d->o.z;
|
||||
const float rz = player1->aboveeye + d->eyeheight;
|
||||
const float r = player1.radius + d.radius;
|
||||
const float dx = player1.o.x - d.o.x;
|
||||
const float dy = player1.o.y - d.o.y;
|
||||
const float dz = player1.o.z - d.o.z;
|
||||
const float rz = player1.aboveeye + d.eyeheight;
|
||||
const float fx = (float)fabs(dx), fy = (float)fabs(dy),
|
||||
fz = (float)fabs(dz);
|
||||
if (fx < r && fy < r && fz < rz && d->state != CS_DEAD) {
|
||||
if (fx < r && fy < r && fz < rz && d.state != CS_DEAD) {
|
||||
if (fx < fy)
|
||||
d->o.y += dy < 0 ? r - fy : -(r - fy); // push aside
|
||||
// push aside
|
||||
d.o = OFMakeVector3D(d.o.x,
|
||||
d.o.y + (dy < 0 ? r - fy : -(r - fy)), d.o.z);
|
||||
else
|
||||
d->o.x += dx < 0 ? r - fx : -(r - fx);
|
||||
d.o = OFMakeVector3D(
|
||||
d.o.x + (dx < 0 ? r - fx : -(r - fx)), d.o.y,
|
||||
d.o.z);
|
||||
}
|
||||
int lagtime = lastmillis - d->lastupdate;
|
||||
int lagtime = lastmillis - d.lastupdate;
|
||||
if (lagtime) {
|
||||
d->plag = (d->plag * 5 + lagtime) / 6;
|
||||
d->lastupdate = lastmillis;
|
||||
d.plag = (d.plag * 5 + lagtime) / 6;
|
||||
d.lastupdate = lastmillis;
|
||||
}
|
||||
}
|
||||
|
||||
// processes any updates from the server
|
||||
void
|
||||
localservertoclient(
|
||||
uchar *buf, int len) // processes any updates from the server
|
||||
localservertoclient(uchar *buf, int len)
|
||||
{
|
||||
if (ENET_NET_TO_HOST_16(*(ushort *)buf) != len)
|
||||
neterr(@"packet length");
|
||||
|
@ -66,7 +72,7 @@ localservertoclient(
|
|||
uchar *p = buf + 2;
|
||||
char text[MAXTRANS];
|
||||
int cn = -1, type;
|
||||
dynent *d = NULL;
|
||||
DynamicEntity *d = nil;
|
||||
bool mapchanged = false;
|
||||
|
||||
while (p < end)
|
||||
|
@ -104,42 +110,42 @@ localservertoclient(
|
|||
break;
|
||||
}
|
||||
|
||||
case SV_POS: // position of another client
|
||||
{
|
||||
case SV_POS: {
|
||||
// position of another client
|
||||
cn = getint(p);
|
||||
d = getclient(cn);
|
||||
if (!d)
|
||||
if (d == nil)
|
||||
return;
|
||||
d->o.x = getint(p) / DMF;
|
||||
d->o.y = getint(p) / DMF;
|
||||
d->o.z = getint(p) / DMF;
|
||||
d->yaw = getint(p) / DAF;
|
||||
d->pitch = getint(p) / DAF;
|
||||
d->roll = getint(p) / DAF;
|
||||
d->vel.x = getint(p) / DVF;
|
||||
d->vel.y = getint(p) / DVF;
|
||||
d->vel.z = getint(p) / DVF;
|
||||
d.o = OFMakeVector3D(
|
||||
getint(p) / DMF, getint(p) / DMF, getint(p) / DMF);
|
||||
d.yaw = getint(p) / DAF;
|
||||
d.pitch = getint(p) / DAF;
|
||||
d.roll = getint(p) / DAF;
|
||||
d.vel = OFMakeVector3D(
|
||||
getint(p) / DVF, getint(p) / DVF, getint(p) / DVF);
|
||||
int f = getint(p);
|
||||
d->strafe = (f & 3) == 3 ? -1 : f & 3;
|
||||
d.strafe = (f & 3) == 3 ? -1 : f & 3;
|
||||
f >>= 2;
|
||||
d->move = (f & 3) == 3 ? -1 : f & 3;
|
||||
d->onfloor = (f >> 2) & 1;
|
||||
d.move = (f & 3) == 3 ? -1 : f & 3;
|
||||
d.onfloor = (f >> 2) & 1;
|
||||
int state = f >> 3;
|
||||
if (state == CS_DEAD && d->state != CS_DEAD)
|
||||
d->lastaction = lastmillis;
|
||||
d->state = state;
|
||||
if (state == CS_DEAD && d.state != CS_DEAD)
|
||||
d.lastaction = lastmillis;
|
||||
d.state = state;
|
||||
if (!demoplayback)
|
||||
updatepos(d);
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_SOUND:
|
||||
playsound(getint(p), &d->o);
|
||||
case SV_SOUND: {
|
||||
OFVector3D loc = d.o;
|
||||
playsound(getint(p), &loc);
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_TEXT:
|
||||
sgetstr();
|
||||
conoutf(@"%s:\f %s", d->name, text);
|
||||
conoutf(@"%@:\f %s", d.name, text);
|
||||
break;
|
||||
|
||||
case SV_MAPCHANGE:
|
||||
|
@ -173,35 +179,35 @@ localservertoclient(
|
|||
break;
|
||||
}
|
||||
|
||||
case SV_INITC2S: // another client either connected or changed
|
||||
// name/team
|
||||
{
|
||||
// another client either connected or changed name/team
|
||||
case SV_INITC2S: {
|
||||
sgetstr();
|
||||
if (d->name[0]) {
|
||||
if (d.name.length > 0) {
|
||||
// already connected
|
||||
if (strcmp(d->name, text))
|
||||
conoutf(@"%s is now known as %s",
|
||||
d->name, text);
|
||||
if (![d.name isEqual:@(text)])
|
||||
conoutf(@"%@ is now known as %s",
|
||||
d.name, text);
|
||||
} else {
|
||||
// new client
|
||||
c2sinit =
|
||||
false; // send new players my info again
|
||||
|
||||
// send new players my info again
|
||||
c2sinit = false;
|
||||
conoutf(@"connected: %s", text);
|
||||
}
|
||||
strcpy_s(d->name, text);
|
||||
d.name = @(text);
|
||||
sgetstr();
|
||||
strcpy_s(d->team, text);
|
||||
d->lifesequence = getint(p);
|
||||
d.team = @(text);
|
||||
d.lifesequence = getint(p);
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_CDIS:
|
||||
cn = getint(p);
|
||||
if (!(d = getclient(cn)))
|
||||
if ((d = getclient(cn)) == nil)
|
||||
break;
|
||||
conoutf(@"player %s disconnected",
|
||||
d->name[0] ? d->name : "[incompatible client]");
|
||||
zapdynent(players[cn]);
|
||||
conoutf(@"player %@ disconnected",
|
||||
d.name.length ? d.name : @"[incompatible client]");
|
||||
players[cn] = [OFNull null];
|
||||
break;
|
||||
|
||||
case SV_SHOT: {
|
||||
|
@ -224,49 +230,51 @@ localservertoclient(
|
|||
int damage = getint(p);
|
||||
int ls = getint(p);
|
||||
if (target == clientnum) {
|
||||
if (ls == player1->lifesequence)
|
||||
if (ls == player1.lifesequence)
|
||||
selfdamage(damage, cn, d);
|
||||
} else
|
||||
playsound(
|
||||
S_PAIN1 + rnd(5), &getclient(target)->o);
|
||||
} else {
|
||||
OFVector3D loc = getclient(target).o;
|
||||
playsound(S_PAIN1 + rnd(5), &loc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_DIED: {
|
||||
int actor = getint(p);
|
||||
if (actor == cn) {
|
||||
conoutf(@"%s suicided", d->name);
|
||||
conoutf(@"%@ suicided", d.name);
|
||||
} else if (actor == clientnum) {
|
||||
int frags;
|
||||
if (isteam(player1->team, d->team)) {
|
||||
if (isteam(player1.team, d.team)) {
|
||||
frags = -1;
|
||||
conoutf(@"you fragged a teammate (%s)",
|
||||
d->name);
|
||||
conoutf(@"you fragged a teammate (%@)",
|
||||
d.name);
|
||||
} else {
|
||||
frags = 1;
|
||||
conoutf(@"you fragged %s", d->name);
|
||||
conoutf(@"you fragged %@", d.name);
|
||||
}
|
||||
addmsg(1, 2, SV_FRAGS, player1->frags += frags);
|
||||
addmsg(
|
||||
1, 2, SV_FRAGS, (player1.frags += frags));
|
||||
} else {
|
||||
dynent *a = getclient(actor);
|
||||
if (a) {
|
||||
if (isteam(a->team, d->name)) {
|
||||
conoutf(@"%s fragged his "
|
||||
@"teammate (%s)",
|
||||
a->name, d->name);
|
||||
} else {
|
||||
conoutf(@"%s fragged %s",
|
||||
a->name, d->name);
|
||||
DynamicEntity *a = getclient(actor);
|
||||
if (a != nil) {
|
||||
if (isteam(a.team, d.name))
|
||||
conoutf(@"%@ fragged his "
|
||||
@"teammate (%@)",
|
||||
a.name, d.name);
|
||||
else
|
||||
conoutf(@"%@ fragged %@",
|
||||
a.name, d.name);
|
||||
}
|
||||
}
|
||||
}
|
||||
playsound(S_DIE1 + rnd(2), &d->o);
|
||||
d->lifesequence++;
|
||||
OFVector3D loc = d.o;
|
||||
playsound(S_DIE1 + rnd(2), &loc);
|
||||
d.lifesequence++;
|
||||
break;
|
||||
}
|
||||
|
||||
case SV_FRAGS:
|
||||
players[cn]->frags = getint(p);
|
||||
[players[cn] setFrags:getint(p)];
|
||||
break;
|
||||
|
||||
case SV_ITEMPICKUP:
|
||||
|
@ -348,13 +356,13 @@ localservertoclient(
|
|||
|
||||
case SV_PONG:
|
||||
addmsg(0, 2, SV_CLIENTPING,
|
||||
player1->ping =
|
||||
(player1->ping * 5 + lastmillis - getint(p)) /
|
||||
player1.ping =
|
||||
(player1.ping * 5 + lastmillis - getint(p)) /
|
||||
6);
|
||||
break;
|
||||
|
||||
case SV_CLIENTPING:
|
||||
players[cn]->ping = getint(p);
|
||||
[players[cn] setPing:getint(p)];
|
||||
break;
|
||||
|
||||
case SV_GAMEMODE:
|
||||
|
|
80
src/cube.h
80
src/cube.h
|
@ -8,6 +8,8 @@
|
|||
|
||||
#include "tools.h"
|
||||
|
||||
@class DynamicEntity;
|
||||
|
||||
@interface Cube: OFObject <OFApplicationDelegate>
|
||||
@property (class, readonly, nonatomic) Cube *sharedInstance;
|
||||
@property (readonly, nonatomic) SDL_Window *window;
|
||||
|
@ -128,41 +130,8 @@ enum {
|
|||
NUMGUNS
|
||||
};
|
||||
|
||||
struct dynent // players & monsters
|
||||
{
|
||||
OFVector3D o, vel; // origin, velocity
|
||||
float yaw, pitch, roll; // used as OFVector3D in one place
|
||||
float maxspeed; // cubes per second, 24 for player
|
||||
bool outsidemap; // from his eyes
|
||||
bool inwater;
|
||||
bool onfloor, jumpnext;
|
||||
int move, strafe;
|
||||
bool k_left, k_right, k_up, k_down; // see input code
|
||||
int timeinair; // used for fake gravity
|
||||
float radius, eyeheight, aboveeye; // bounding box size
|
||||
int lastupdate, plag, ping;
|
||||
int lifesequence; // sequence id for each respawn, used in damage test
|
||||
int state; // one of CS_* below
|
||||
int frags;
|
||||
int health, armour, armourtype, quadmillis;
|
||||
int gunselect, gunwait;
|
||||
int lastaction, lastattackgun, lastmove;
|
||||
bool attacking;
|
||||
int ammo[NUMGUNS];
|
||||
int monsterstate; // one of M_* below, M_NONE means human
|
||||
int mtype; // see monster.cpp
|
||||
dynent *enemy; // monster wants to kill this entity
|
||||
float targetyaw; // monster wants to look in this direction
|
||||
bool blocked, moving; // used by physics to signal ai
|
||||
int trigger; // millis at which transition to another monsterstate takes
|
||||
// place
|
||||
OFVector3D attacktarget; // delayed attacks
|
||||
int anger; // how many times already hit by fellow monster
|
||||
string name, team;
|
||||
};
|
||||
|
||||
#define SAVEGAMEVERSION \
|
||||
4 // bump if dynent/netprotocol changes or any other savegame/demo data
|
||||
// bump if dynent/netprotocol changes or any other savegame/demo data
|
||||
#define SAVEGAMEVERSION 4
|
||||
|
||||
enum { A_BLUE, A_GREEN, A_YELLOW }; // armour types... take 20/40/60 % off
|
||||
enum {
|
||||
|
@ -281,8 +250,6 @@ struct vertex {
|
|||
uchar r, g, b, a;
|
||||
};
|
||||
|
||||
typedef vector<dynent *> dvector;
|
||||
|
||||
// globals ooh naughty
|
||||
|
||||
extern sqr *world,
|
||||
|
@ -290,9 +257,10 @@ extern sqr *world,
|
|||
extern header hdr; // current map header
|
||||
extern int sfactor, ssize; // ssize = 2^sfactor
|
||||
extern int cubicsize, mipsize; // cubicsize = ssize^2
|
||||
extern dynent
|
||||
*player1; // special client ent that receives input and acts as camera
|
||||
extern dvector players; // all the other clients (in multiplayer)
|
||||
// special client ent that receives input and acts as camera
|
||||
extern DynamicEntity *player1;
|
||||
// all the other clients (in multiplayer)
|
||||
extern OFMutableArray *players;
|
||||
extern bool editmode;
|
||||
extern vector<entity> ents; // map entities
|
||||
extern OFVector3D worldpos; // current target of the crosshair in the world
|
||||
|
@ -318,28 +286,30 @@ extern bool demoplayback;
|
|||
#define dotprod(u, v) ((u).x * (v).x + (u).y * (v).y + (u).z * (v).z)
|
||||
#define vmul(u, f) \
|
||||
{ \
|
||||
(u).x *= (f); \
|
||||
(u).y *= (f); \
|
||||
(u).z *= (f); \
|
||||
OFVector3D tmp_ = u; \
|
||||
float tmp2_ = f; \
|
||||
u = OFMakeVector3D( \
|
||||
tmp_.x * tmp2_, tmp_.y * tmp2_, tmp_.z * tmp2_); \
|
||||
}
|
||||
#define vdiv(u, f) \
|
||||
{ \
|
||||
(u).x /= (f); \
|
||||
(u).y /= (f); \
|
||||
(u).z /= (f); \
|
||||
OFVector3D tmp_ = u; \
|
||||
float tmp2_ = f; \
|
||||
u = OFMakeVector3D( \
|
||||
tmp_.x / tmp2_, tmp_.y / tmp2_, tmp_.z / tmp2_); \
|
||||
}
|
||||
#define vadd(u, v) \
|
||||
{ \
|
||||
(u).x += (v).x; \
|
||||
(u).y += (v).y; \
|
||||
(u).z += (v).z; \
|
||||
};
|
||||
OFVector3D tmp_ = u; \
|
||||
u = OFMakeVector3D( \
|
||||
tmp_.x + (v).x, tmp_.y + (v).y, tmp_.z + (v).z); \
|
||||
}
|
||||
#define vsub(u, v) \
|
||||
{ \
|
||||
(u).x -= (v).x; \
|
||||
(u).y -= (v).y; \
|
||||
(u).z -= (v).z; \
|
||||
};
|
||||
OFVector3D tmp_ = u; \
|
||||
u = OFMakeVector3D( \
|
||||
tmp_.x - (v).x, tmp_.y - (v).y, tmp_.z - (v).z); \
|
||||
}
|
||||
#define vdist(d, v, e, s) \
|
||||
OFVector3D v = s; \
|
||||
vsub(v, e); \
|
||||
|
@ -370,7 +340,7 @@ extern bool demoplayback;
|
|||
#define m_sp (gamemode < 0)
|
||||
#define m_dmsp (gamemode == -1)
|
||||
#define m_classicsp (gamemode == -2)
|
||||
#define isteam(a, b) (m_teammode && strcmp(a, b) == 0)
|
||||
#define isteam(a, b) (m_teammode && [a isEqual:b])
|
||||
|
||||
enum // function signatures for script functions, see command.cpp
|
||||
{
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
bool editmode = false;
|
||||
|
||||
// the current selection, used by almost all editing commands
|
||||
|
@ -52,7 +54,7 @@ VAR(editing, 0, 0, 1);
|
|||
void
|
||||
toggleedit()
|
||||
{
|
||||
if (player1->state == CS_DEAD)
|
||||
if (player1.state == CS_DEAD)
|
||||
return; // do not allow dead players to edit to avoid state
|
||||
// confusion
|
||||
if (!editmode && !allowedittoggle())
|
||||
|
@ -63,7 +65,7 @@ toggleedit()
|
|||
} else {
|
||||
resettagareas(); // clear trigger areas to allow them to be
|
||||
// edited
|
||||
player1->health = 100;
|
||||
player1.health = 100;
|
||||
if (m_classicsp)
|
||||
monsterclear(); // all monsters back at their spawns for
|
||||
// editing
|
||||
|
@ -150,7 +152,7 @@ sheight(
|
|||
void
|
||||
cursorupdate() // called every frame from hud
|
||||
{
|
||||
flrceil = ((int)(player1->pitch >= 0)) * 2;
|
||||
flrceil = ((int)(player1.pitch >= 0)) * 2;
|
||||
|
||||
volatile float x =
|
||||
worldpos.x; // volatile needed to prevent msvc7 optimizer bug?
|
||||
|
@ -166,8 +168,8 @@ cursorupdate() // called every frame from hud
|
|||
|
||||
// selected wall
|
||||
if (fabs(sheight(s, s, z) - z) > 1) {
|
||||
x += x > player1->o.x ? 0.5f : -0.5f; // find right wall cube
|
||||
y += y > player1->o.y ? 0.5f : -0.5f;
|
||||
x += x > player1.o.x ? 0.5f : -0.5f; // find right wall cube
|
||||
y += y > player1.o.y ? 0.5f : -0.5f;
|
||||
|
||||
cx = (int)x;
|
||||
cy = (int)y;
|
||||
|
@ -322,9 +324,9 @@ tofronttex() // maintain most recently used of the texture lists when applying
|
|||
}
|
||||
|
||||
void
|
||||
editdrag(bool isdown)
|
||||
editdrag(bool isDown)
|
||||
{
|
||||
if (dragging = isdown) {
|
||||
if ((dragging = isDown)) {
|
||||
lastx = cx;
|
||||
lasty = cy;
|
||||
lasth = ch;
|
||||
|
@ -602,7 +604,7 @@ newent(OFString *what, OFString *a1, OFString *a2, OFString *a3, OFString *a4)
|
|||
{
|
||||
EDITSEL;
|
||||
@autoreleasepool {
|
||||
newentity(sel.x, sel.y, (int)player1->o.z, what,
|
||||
newentity(sel.x, sel.y, (int)player1.o.z, what,
|
||||
(int)[a1 longLongValueWithBase:0],
|
||||
(int)[a2 longLongValueWithBase:0],
|
||||
(int)[a3 longLongValueWithBase:0],
|
||||
|
|
102
src/entities.mm
102
src/entities.mm
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
#import "MapModelInfo.h"
|
||||
|
||||
vector<entity> ents;
|
||||
|
@ -122,14 +123,14 @@ struct itemstat {
|
|||
void
|
||||
baseammo(int gun)
|
||||
{
|
||||
player1->ammo[gun] = itemstats[gun - 1].add * 2;
|
||||
player1.ammo[gun] = itemstats[gun - 1].add * 2;
|
||||
}
|
||||
|
||||
// these two functions are called when the server acknowledges that you really
|
||||
// picked up the item (in multiplayer someone may grab it before you).
|
||||
|
||||
void
|
||||
radditem(int i, int &v)
|
||||
static int
|
||||
radditem(int i, int v)
|
||||
{
|
||||
itemstat &is = itemstats[ents[i].type - I_SHELLS];
|
||||
ents[i].spawned = false;
|
||||
|
@ -137,43 +138,44 @@ radditem(int i, int &v)
|
|||
if (v > is.max)
|
||||
v = is.max;
|
||||
playsoundc(is.sound);
|
||||
return v;
|
||||
}
|
||||
|
||||
void
|
||||
realpickup(int n, dynent *d)
|
||||
realpickup(int n, DynamicEntity *d)
|
||||
{
|
||||
switch (ents[n].type) {
|
||||
case I_SHELLS:
|
||||
radditem(n, d->ammo[1]);
|
||||
d.ammo[1] = radditem(n, d.ammo[1]);
|
||||
break;
|
||||
case I_BULLETS:
|
||||
radditem(n, d->ammo[2]);
|
||||
d.ammo[2] = radditem(n, d.ammo[2]);
|
||||
break;
|
||||
case I_ROCKETS:
|
||||
radditem(n, d->ammo[3]);
|
||||
d.ammo[3] = radditem(n, d.ammo[3]);
|
||||
break;
|
||||
case I_ROUNDS:
|
||||
radditem(n, d->ammo[4]);
|
||||
d.ammo[4] = radditem(n, d.ammo[4]);
|
||||
break;
|
||||
case I_HEALTH:
|
||||
radditem(n, d->health);
|
||||
d.health = radditem(n, d.health);
|
||||
break;
|
||||
case I_BOOST:
|
||||
radditem(n, d->health);
|
||||
d.health = radditem(n, d.health);
|
||||
break;
|
||||
|
||||
case I_GREENARMOUR:
|
||||
radditem(n, d->armour);
|
||||
d->armourtype = A_GREEN;
|
||||
d.armour = radditem(n, d.armour);
|
||||
d.armourtype = A_GREEN;
|
||||
break;
|
||||
|
||||
case I_YELLOWARMOUR:
|
||||
radditem(n, d->armour);
|
||||
d->armourtype = A_YELLOW;
|
||||
d.armour = radditem(n, d.armour);
|
||||
d.armourtype = A_YELLOW;
|
||||
break;
|
||||
|
||||
case I_QUAD:
|
||||
radditem(n, d->quadmillis);
|
||||
d.quadmillis = radditem(n, d.quadmillis);
|
||||
conoutf(@"you got the quad!");
|
||||
break;
|
||||
}
|
||||
|
@ -182,20 +184,20 @@ realpickup(int n, dynent *d)
|
|||
// these functions are called when the client touches the item
|
||||
|
||||
void
|
||||
additem(int i, int &v, int spawnsec)
|
||||
additem(int i, int v, int spawnsec)
|
||||
{
|
||||
if (v < itemstats[ents[i].type - I_SHELLS]
|
||||
.max) // don't pick up if not needed
|
||||
{
|
||||
addmsg(1, 3, SV_ITEMPICKUP, i,
|
||||
m_classicsp ? 100000
|
||||
: spawnsec); // first ask the server for an ack
|
||||
ents[i].spawned = false; // even if someone else gets it first
|
||||
// don't pick up if not needed
|
||||
if (v < itemstats[ents[i].type - I_SHELLS].max) {
|
||||
// first ask the server for an ack even if someone else gets it
|
||||
// first
|
||||
addmsg(1, 3, SV_ITEMPICKUP, i, m_classicsp ? 100000 : spawnsec);
|
||||
ents[i].spawned = false;
|
||||
}
|
||||
}
|
||||
|
||||
// also used by monsters
|
||||
void
|
||||
teleport(int n, dynent *d) // also used by monsters
|
||||
teleport(int n, DynamicEntity *d)
|
||||
{
|
||||
int e = -1, tag = ents[n].attr1, beenhere = -1;
|
||||
for (;;) {
|
||||
|
@ -207,12 +209,10 @@ teleport(int n, dynent *d) // also used by monsters
|
|||
if (beenhere < 0)
|
||||
beenhere = e;
|
||||
if (ents[e].attr2 == tag) {
|
||||
d->o.x = ents[e].x;
|
||||
d->o.y = ents[e].y;
|
||||
d->o.z = ents[e].z;
|
||||
d->yaw = ents[e].attr1;
|
||||
d->pitch = 0;
|
||||
d->vel.x = d->vel.y = d->vel.z = 0;
|
||||
d.o = OFMakeVector3D(ents[e].x, ents[e].y, ents[e].z);
|
||||
d.yaw = ents[e].attr1;
|
||||
d.pitch = 0;
|
||||
d.vel = OFMakeVector3D(0, 0, 0);
|
||||
entinmap(d);
|
||||
playsoundc(S_TELEPORT);
|
||||
break;
|
||||
|
@ -221,46 +221,48 @@ teleport(int n, dynent *d) // also used by monsters
|
|||
}
|
||||
|
||||
void
|
||||
pickup(int n, dynent *d)
|
||||
pickup(int n, DynamicEntity *d)
|
||||
{
|
||||
int np = 1;
|
||||
loopv(players) if (players[i]) np++;
|
||||
np = np < 3 ? 4 : (np > 4 ? 2 : 3); // spawn times are dependent on
|
||||
// number of players
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
np++;
|
||||
// spawn times are dependent on number of players
|
||||
np = np < 3 ? 4 : (np > 4 ? 2 : 3);
|
||||
int ammo = np * 2;
|
||||
switch (ents[n].type) {
|
||||
case I_SHELLS:
|
||||
additem(n, d->ammo[1], ammo);
|
||||
additem(n, d.ammo[1], ammo);
|
||||
break;
|
||||
case I_BULLETS:
|
||||
additem(n, d->ammo[2], ammo);
|
||||
additem(n, d.ammo[2], ammo);
|
||||
break;
|
||||
case I_ROCKETS:
|
||||
additem(n, d->ammo[3], ammo);
|
||||
additem(n, d.ammo[3], ammo);
|
||||
break;
|
||||
case I_ROUNDS:
|
||||
additem(n, d->ammo[4], ammo);
|
||||
additem(n, d.ammo[4], ammo);
|
||||
break;
|
||||
case I_HEALTH:
|
||||
additem(n, d->health, np * 5);
|
||||
additem(n, d.health, np * 5);
|
||||
break;
|
||||
case I_BOOST:
|
||||
additem(n, d->health, 60);
|
||||
additem(n, d.health, 60);
|
||||
break;
|
||||
|
||||
case I_GREENARMOUR:
|
||||
// (100h/100g only absorbs 166 damage)
|
||||
if (d->armourtype == A_YELLOW && d->armour > 66)
|
||||
if (d.armourtype == A_YELLOW && d.armour > 66)
|
||||
break;
|
||||
additem(n, d->armour, 20);
|
||||
additem(n, d.armour, 20);
|
||||
break;
|
||||
|
||||
case I_YELLOWARMOUR:
|
||||
additem(n, d->armour, 20);
|
||||
additem(n, d.armour, 20);
|
||||
break;
|
||||
|
||||
case I_QUAD:
|
||||
additem(n, d->quadmillis, 60);
|
||||
additem(n, d.quadmillis, 60);
|
||||
break;
|
||||
|
||||
case CARROT:
|
||||
|
@ -286,8 +288,8 @@ pickup(int n, dynent *d)
|
|||
lastjumppad = lastmillis;
|
||||
OFVector3D v = OFMakeVector3D((int)(char)ents[n].attr3 / 10.0f,
|
||||
(int)(char)ents[n].attr2 / 10.0f, ents[n].attr1 / 10.0f);
|
||||
player1->vel.z = 0;
|
||||
vadd(player1->vel, v);
|
||||
player1.vel = OFMakeVector3D(player1.vel.x, player1.vel.y, 0);
|
||||
vadd(player1.vel, v);
|
||||
playsoundc(S_JUMPPAD);
|
||||
break;
|
||||
}
|
||||
|
@ -309,8 +311,8 @@ checkitems()
|
|||
if (OUTBORD(e.x, e.y))
|
||||
continue;
|
||||
OFVector3D v = OFMakeVector3D(
|
||||
e.x, e.y, (float)S(e.x, e.y)->floor + player1->eyeheight);
|
||||
vdist(dist, t, player1->o, v);
|
||||
e.x, e.y, (float)S(e.x, e.y)->floor + player1.eyeheight);
|
||||
vdist(dist, t, player1.o, v);
|
||||
if (dist < (e.type == TELEPORT ? 4 : 2.5))
|
||||
pickup(i, player1);
|
||||
}
|
||||
|
@ -319,8 +321,8 @@ checkitems()
|
|||
void
|
||||
checkquad(int time)
|
||||
{
|
||||
if (player1->quadmillis && (player1->quadmillis -= time) < 0) {
|
||||
player1->quadmillis = 0;
|
||||
if (player1.quadmillis && (player1.quadmillis -= time) < 0) {
|
||||
player1.quadmillis = 0;
|
||||
playsoundc(S_PUPOUT);
|
||||
conoutf(@"quad damage is over");
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ executable('client',
|
|||
'Alias.m',
|
||||
'Command.mm',
|
||||
'Cube.mm',
|
||||
'DynamicEntity.mm',
|
||||
'Identifier.m',
|
||||
'KeyMapping.m',
|
||||
'MD2.mm',
|
||||
|
|
297
src/monster.mm
297
src/monster.mm
|
@ -2,22 +2,27 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
dvector monsters;
|
||||
int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
static OFMutableArray<DynamicEntity *> *monsters;
|
||||
static int nextmonster, spawnremain, numkilled, monstertotal, mtimestart;
|
||||
|
||||
VARF(skill, 1, 3, 10, conoutf(@"skill is now %d", skill));
|
||||
|
||||
dvector &
|
||||
OFArray<DynamicEntity *> *
|
||||
getmonsters()
|
||||
{
|
||||
return monsters;
|
||||
}
|
||||
|
||||
// for savegames
|
||||
void
|
||||
restoremonsterstate()
|
||||
{
|
||||
loopv(monsters) if (monsters[i]->state == CS_DEAD) numkilled++;
|
||||
} // for savegames
|
||||
for (DynamicEntity *monster in monsters)
|
||||
if (monster.state == CS_DEAD)
|
||||
numkilled++;
|
||||
}
|
||||
|
||||
#define TOTMFREQ 13
|
||||
#define NUMMONSTERTYPES 8
|
||||
|
@ -49,40 +54,43 @@ monstertypes[NUMMONSTERTYPES] = {
|
|||
@"a goblin", @"monster/goblin" },
|
||||
};
|
||||
|
||||
dynent *
|
||||
DynamicEntity *
|
||||
basicmonster(int type, int yaw, int state, int trigger, int move)
|
||||
{
|
||||
if (type >= NUMMONSTERTYPES) {
|
||||
conoutf(@"warning: unknown monster in spawn: %d", type);
|
||||
type = 0;
|
||||
}
|
||||
dynent *m = newdynent();
|
||||
monstertype *t = &monstertypes[m->mtype = type];
|
||||
m->eyeheight = 2.0f;
|
||||
m->aboveeye = 1.9f;
|
||||
m->radius *= t->bscale / 10.0f;
|
||||
m->eyeheight *= t->bscale / 10.0f;
|
||||
m->aboveeye *= t->bscale / 10.0f;
|
||||
m->monsterstate = state;
|
||||
DynamicEntity *m = newdynent();
|
||||
monstertype *t = &monstertypes[(m.mtype = type)];
|
||||
m.eyeheight = 2.0f;
|
||||
m.aboveeye = 1.9f;
|
||||
m.radius *= t->bscale / 10.0f;
|
||||
m.eyeheight *= t->bscale / 10.0f;
|
||||
m.aboveeye *= t->bscale / 10.0f;
|
||||
m.monsterstate = state;
|
||||
if (state != M_SLEEP)
|
||||
spawnplayer(m);
|
||||
m->trigger = lastmillis + trigger;
|
||||
m->targetyaw = m->yaw = (float)yaw;
|
||||
m->move = move;
|
||||
m->enemy = player1;
|
||||
m->gunselect = t->gun;
|
||||
m->maxspeed = (float)t->speed;
|
||||
m->health = t->health;
|
||||
m->armour = 0;
|
||||
loopi(NUMGUNS) m->ammo[i] = 10000;
|
||||
m->pitch = 0;
|
||||
m->roll = 0;
|
||||
m->state = CS_ALIVE;
|
||||
m->anger = 0;
|
||||
@autoreleasepool {
|
||||
strcpy_s(m->name, t->name.UTF8String);
|
||||
}
|
||||
monsters.add(m);
|
||||
m.trigger = lastmillis + trigger;
|
||||
m.targetyaw = m.yaw = (float)yaw;
|
||||
m.move = move;
|
||||
m.enemy = player1;
|
||||
m.gunselect = t->gun;
|
||||
m.maxspeed = (float)t->speed;
|
||||
m.health = t->health;
|
||||
m.armour = 0;
|
||||
loopi(NUMGUNS) m.ammo[i] = 10000;
|
||||
m.pitch = 0;
|
||||
m.roll = 0;
|
||||
m.state = CS_ALIVE;
|
||||
m.anger = 0;
|
||||
m.name = t->name;
|
||||
|
||||
if (monsters == nil)
|
||||
monsters = [[OFMutableArray alloc] init];
|
||||
|
||||
[monsters addObject:m];
|
||||
|
||||
return m;
|
||||
}
|
||||
|
||||
|
@ -99,12 +107,12 @@ spawnmonster() // spawn a random monster according to freq distribution in DMSP
|
|||
basicmonster(type, rnd(360), M_SEARCH, 1000, 1);
|
||||
}
|
||||
|
||||
// called after map start of when toggling edit mode to reset/spawn all
|
||||
// monsters to initial state
|
||||
void
|
||||
monsterclear() // called after map start of when toggling edit mode to
|
||||
// reset/spawn all monsters to initial state
|
||||
monsterclear()
|
||||
{
|
||||
loopv(monsters) free(monsters[i]);
|
||||
monsters.setsize(0);
|
||||
[monsters removeAllObjects];
|
||||
numkilled = 0;
|
||||
monstertotal = 0;
|
||||
spawnremain = 0;
|
||||
|
@ -115,20 +123,18 @@ monsterclear() // called after map start of when toggling edit mode to
|
|||
mtimestart = lastmillis;
|
||||
loopv(ents) if (ents[i].type == MONSTER)
|
||||
{
|
||||
dynent *m = basicmonster(
|
||||
DynamicEntity *m = basicmonster(
|
||||
ents[i].attr2, ents[i].attr1, M_SLEEP, 100, 0);
|
||||
m->o.x = ents[i].x;
|
||||
m->o.y = ents[i].y;
|
||||
m->o.z = ents[i].z;
|
||||
m.o = OFMakeVector3D(ents[i].x, ents[i].y, ents[i].z);
|
||||
entinmap(m);
|
||||
monstertotal++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// height-correct line of sight for monster shooting/seeing
|
||||
bool
|
||||
los(float lx, float ly, float lz, float bx, float by, float bz,
|
||||
OFVector3D &v) // height-correct line of sight for monster shooting/seeing
|
||||
los(float lx, float ly, float lz, float bx, float by, float bz, OFVector3D &v)
|
||||
{
|
||||
if (OUTBORD((int)lx, (int)ly) || OUTBORD((int)bx, (int)by))
|
||||
return false;
|
||||
|
@ -164,11 +170,11 @@ los(float lx, float ly, float lz, float bx, float by, float bz,
|
|||
}
|
||||
|
||||
bool
|
||||
enemylos(dynent *m, OFVector3D &v)
|
||||
enemylos(DynamicEntity *m, OFVector3D &v)
|
||||
{
|
||||
v = m->o;
|
||||
return los(m->o.x, m->o.y, m->o.z, m->enemy->o.x, m->enemy->o.y,
|
||||
m->enemy->o.z, v);
|
||||
v = m.o;
|
||||
return los(
|
||||
m.o.x, m.o.y, m.o.z, m.enemy.o.x, m.enemy.o.y, m.enemy.o.z, v);
|
||||
}
|
||||
|
||||
// monster AI is sequenced using transitions: they are in a particular state
|
||||
|
@ -178,73 +184,72 @@ enemylos(dynent *m, OFVector3D &v)
|
|||
// parametrized by difficulty level (skill), faster transitions means quicker
|
||||
// decision making means tougher AI.
|
||||
|
||||
// n = at skill 0, n/2 = at skill 10, r = added random factor
|
||||
void
|
||||
transition(dynent *m, int state, int moving, int n,
|
||||
int r) // n = at skill 0, n/2 = at skill 10, r = added random factor
|
||||
transition(DynamicEntity *m, int state, int moving, int n, int r)
|
||||
{
|
||||
m->monsterstate = state;
|
||||
m->move = moving;
|
||||
m.monsterstate = state;
|
||||
m.move = moving;
|
||||
n = n * 130 / 100;
|
||||
m->trigger = lastmillis + n - skill * (n / 16) + rnd(r + 1);
|
||||
m.trigger = lastmillis + n - skill * (n / 16) + rnd(r + 1);
|
||||
}
|
||||
|
||||
void
|
||||
normalise(dynent *m, float angle)
|
||||
normalise(DynamicEntity *m, float angle)
|
||||
{
|
||||
while (m->yaw < angle - 180.0f)
|
||||
m->yaw += 360.0f;
|
||||
while (m->yaw > angle + 180.0f)
|
||||
m->yaw -= 360.0f;
|
||||
while (m.yaw < angle - 180.0f)
|
||||
m.yaw += 360.0f;
|
||||
while (m.yaw > angle + 180.0f)
|
||||
m.yaw -= 360.0f;
|
||||
}
|
||||
|
||||
// main AI thinking routine, called every frame for every monster
|
||||
void
|
||||
monsteraction(
|
||||
dynent *m) // main AI thinking routine, called every frame for every monster
|
||||
monsteraction(DynamicEntity *m)
|
||||
{
|
||||
if (m->enemy->state == CS_DEAD) {
|
||||
m->enemy = player1;
|
||||
m->anger = 0;
|
||||
if (m.enemy.state == CS_DEAD) {
|
||||
m.enemy = player1;
|
||||
m.anger = 0;
|
||||
}
|
||||
normalise(m, m->targetyaw);
|
||||
if (m->targetyaw > m->yaw) // slowly turn monster towards his target
|
||||
{
|
||||
m->yaw += curtime * 0.5f;
|
||||
if (m->targetyaw < m->yaw)
|
||||
m->yaw = m->targetyaw;
|
||||
normalise(m, m.targetyaw);
|
||||
// slowly turn monster towards his target
|
||||
if (m.targetyaw > m.yaw) {
|
||||
m.yaw += curtime * 0.5f;
|
||||
if (m.targetyaw < m.yaw)
|
||||
m.yaw = m.targetyaw;
|
||||
} else {
|
||||
m->yaw -= curtime * 0.5f;
|
||||
if (m->targetyaw > m->yaw)
|
||||
m->yaw = m->targetyaw;
|
||||
m.yaw -= curtime * 0.5f;
|
||||
if (m.targetyaw > m.yaw)
|
||||
m.yaw = m.targetyaw;
|
||||
}
|
||||
|
||||
vdist(disttoenemy, vectoenemy, m->o, m->enemy->o);
|
||||
m->pitch = atan2(m->enemy->o.z - m->o.z, disttoenemy) * 180 / PI;
|
||||
vdist(disttoenemy, vectoenemy, m.o, m.enemy.o);
|
||||
m.pitch = atan2(m.enemy.o.z - m.o.z, disttoenemy) * 180 / PI;
|
||||
|
||||
// special case: if we run into scenery
|
||||
if (m->blocked) {
|
||||
m->blocked = false;
|
||||
if (m.blocked) {
|
||||
m.blocked = false;
|
||||
// try to jump over obstackle (rare)
|
||||
if (!rnd(20000 / monstertypes[m->mtype].speed))
|
||||
m->jumpnext = true;
|
||||
if (!rnd(20000 / monstertypes[m.mtype].speed))
|
||||
m.jumpnext = true;
|
||||
// search for a way around (common)
|
||||
else if (m->trigger < lastmillis &&
|
||||
(m->monsterstate != M_HOME || !rnd(5))) {
|
||||
else if (m.trigger < lastmillis &&
|
||||
(m.monsterstate != M_HOME || !rnd(5))) {
|
||||
// patented "random walk" AI pathfinding (tm) ;)
|
||||
m->targetyaw += 180 + rnd(180);
|
||||
m.targetyaw += 180 + rnd(180);
|
||||
transition(m, M_SEARCH, 1, 400, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
float enemyyaw =
|
||||
-(float)atan2(m->enemy->o.x - m->o.x, m->enemy->o.y - m->o.y) / PI *
|
||||
180 +
|
||||
-(float)atan2(m.enemy.o.x - m.o.x, m.enemy.o.y - m.o.y) / PI * 180 +
|
||||
180;
|
||||
|
||||
switch (m->monsterstate) {
|
||||
switch (m.monsterstate) {
|
||||
case M_PAIN:
|
||||
case M_ATTACKING:
|
||||
case M_SEARCH:
|
||||
if (m->trigger < lastmillis)
|
||||
if (m.trigger < lastmillis)
|
||||
transition(m, M_HOME, 1, 100, 200);
|
||||
break;
|
||||
|
||||
|
@ -255,54 +260,53 @@ monsteraction(
|
|||
if (editmode || !enemylos(m, target))
|
||||
return; // skip running physics
|
||||
normalise(m, enemyyaw);
|
||||
float angle = (float)fabs(enemyyaw - m->yaw);
|
||||
float angle = (float)fabs(enemyyaw - m.yaw);
|
||||
if (disttoenemy < 8 // the better the angle to the player, the
|
||||
// further the monster can see/hear
|
||||
|| (disttoenemy < 16 && angle < 135) ||
|
||||
(disttoenemy < 32 && angle < 90) ||
|
||||
(disttoenemy < 64 && angle < 45) || angle < 10) {
|
||||
transition(m, M_HOME, 1, 500, 200);
|
||||
playsound(S_GRUNT1 + rnd(2), &m->o);
|
||||
OFVector3D loc = m.o;
|
||||
playsound(S_GRUNT1 + rnd(2), &loc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case M_AIMING: // this state is the delay between wanting to shoot and
|
||||
// actually firing
|
||||
if (m->trigger < lastmillis) {
|
||||
m->lastaction = 0;
|
||||
m->attacking = true;
|
||||
shoot(m, m->attacktarget);
|
||||
case M_AIMING:
|
||||
// this state is the delay between wanting to shoot and actually
|
||||
// firing
|
||||
if (m.trigger < lastmillis) {
|
||||
m.lastaction = 0;
|
||||
m.attacking = true;
|
||||
shoot(m, m.attacktarget);
|
||||
transition(m, M_ATTACKING, 0, 600, 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case M_HOME: // monster has visual contact, heads straight for player
|
||||
// and may want to shoot at any time
|
||||
m->targetyaw = enemyyaw;
|
||||
if (m->trigger < lastmillis) {
|
||||
case M_HOME:
|
||||
// monster has visual contact, heads straight for player and
|
||||
// may want to shoot at any time
|
||||
m.targetyaw = enemyyaw;
|
||||
if (m.trigger < lastmillis) {
|
||||
OFVector3D target;
|
||||
if (!enemylos(
|
||||
m, target)) // no visual contact anymore, let
|
||||
// monster get as close as possible
|
||||
// then search for player
|
||||
{
|
||||
if (!enemylos(m, target)) {
|
||||
// no visual contact anymore, let monster get
|
||||
// as close as possible then search for player
|
||||
transition(m, M_HOME, 1, 800, 500);
|
||||
} else // the closer the monster is the more likely he
|
||||
} else {
|
||||
// the closer the monster is the more likely he
|
||||
// wants to shoot
|
||||
{
|
||||
if (!rnd((int)disttoenemy / 3 + 1) &&
|
||||
m->enemy->state ==
|
||||
CS_ALIVE) // get ready to fire
|
||||
{
|
||||
m->attacktarget = target;
|
||||
m.enemy.state == CS_ALIVE) {
|
||||
// get ready to fire
|
||||
m.attacktarget = target;
|
||||
transition(m, M_AIMING, 0,
|
||||
monstertypes[m->mtype].lag, 10);
|
||||
} else // track player some more
|
||||
{
|
||||
monstertypes[m.mtype].lag, 10);
|
||||
} else
|
||||
// track player some more
|
||||
transition(m, M_HOME, 1,
|
||||
monstertypes[m->mtype].rate, 0);
|
||||
}
|
||||
monstertypes[m.mtype].rate, 0);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -312,38 +316,40 @@ monsteraction(
|
|||
}
|
||||
|
||||
void
|
||||
monsterpain(dynent *m, int damage, dynent *d)
|
||||
monsterpain(DynamicEntity *m, int damage, DynamicEntity *d)
|
||||
{
|
||||
// a monster hit us
|
||||
if (d->monsterstate) {
|
||||
if (d.monsterstate) {
|
||||
// guard for RL guys shooting themselves :)
|
||||
if (m != d) {
|
||||
// don't attack straight away, first get angry
|
||||
m->anger++;
|
||||
int anger =
|
||||
m->mtype == d->mtype ? m->anger / 2 : m->anger;
|
||||
if (anger >= monstertypes[m->mtype].loyalty)
|
||||
m.anger++;
|
||||
int anger = m.mtype == d.mtype ? m.anger / 2 : m.anger;
|
||||
if (anger >= monstertypes[m.mtype].loyalty)
|
||||
// monster infight if very angry
|
||||
m->enemy = d;
|
||||
m.enemy = d;
|
||||
}
|
||||
} else {
|
||||
// player hit us
|
||||
m->anger = 0;
|
||||
m->enemy = d;
|
||||
m.anger = 0;
|
||||
m.enemy = d;
|
||||
}
|
||||
// in this state monster won't attack
|
||||
transition(m, M_PAIN, 0, monstertypes[m->mtype].pain, 200);
|
||||
if ((m->health -= damage) <= 0) {
|
||||
m->state = CS_DEAD;
|
||||
m->lastaction = lastmillis;
|
||||
transition(m, M_PAIN, 0, monstertypes[m.mtype].pain, 200);
|
||||
if ((m.health -= damage) <= 0) {
|
||||
m.state = CS_DEAD;
|
||||
m.lastaction = lastmillis;
|
||||
numkilled++;
|
||||
player1->frags = numkilled;
|
||||
playsound(monstertypes[m->mtype].diesound, &m->o);
|
||||
player1.frags = numkilled;
|
||||
OFVector3D loc = m.o;
|
||||
playsound(monstertypes[m.mtype].diesound, &loc);
|
||||
int remain = monstertotal - numkilled;
|
||||
if (remain > 0 && remain <= 5)
|
||||
conoutf(@"only %d monster(s) remaining", remain);
|
||||
} else
|
||||
playsound(monstertypes[m->mtype].painsound, &m->o);
|
||||
} else {
|
||||
OFVector3D loc = m.o;
|
||||
playsound(monstertypes[m.mtype].painsound, &loc);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -370,8 +376,8 @@ monsterthink()
|
|||
if (monstertotal && !spawnremain && numkilled == monstertotal)
|
||||
endsp(true);
|
||||
|
||||
loopv(ents) // equivalent of player entity touch, but only teleports are
|
||||
// used
|
||||
// equivalent of player entity touch, but only teleports are used
|
||||
loopv(ents)
|
||||
{
|
||||
entity &e = ents[i];
|
||||
if (e.type != TELEPORT)
|
||||
|
@ -380,33 +386,32 @@ monsterthink()
|
|||
continue;
|
||||
OFVector3D v =
|
||||
OFMakeVector3D(e.x, e.y, (float)S(e.x, e.y)->floor);
|
||||
loopv(monsters)
|
||||
{
|
||||
if (monsters[i]->state == CS_DEAD) {
|
||||
if (lastmillis - monsters[i]->lastaction <
|
||||
2000) {
|
||||
monsters[i]->move = 0;
|
||||
moveplayer(monsters[i], 1, false);
|
||||
for (DynamicEntity *monster in monsters) {
|
||||
if (monster.state == CS_DEAD) {
|
||||
if (lastmillis - monster.lastaction < 2000) {
|
||||
monster.move = 0;
|
||||
moveplayer(monster, 1, false);
|
||||
}
|
||||
} else {
|
||||
v.z += monsters[i]->eyeheight;
|
||||
vdist(dist, t, monsters[i]->o, v);
|
||||
v.z -= monsters[i]->eyeheight;
|
||||
v.z += monster.eyeheight;
|
||||
vdist(dist, t, monster.o, v);
|
||||
v.z -= monster.eyeheight;
|
||||
if (dist < 4)
|
||||
teleport(
|
||||
(int)(&e - &ents[0]), monsters[i]);
|
||||
teleport((int)(&e - &ents[0]), monster);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loopv(monsters) if (monsters[i]->state == CS_ALIVE)
|
||||
monsteraction(monsters[i]);
|
||||
for (DynamicEntity *monster in monsters)
|
||||
if (monster.state == CS_ALIVE)
|
||||
monsteraction(monster);
|
||||
}
|
||||
|
||||
void
|
||||
monsterrender()
|
||||
{
|
||||
loopv(monsters) renderclient(monsters[i], false,
|
||||
monstertypes[monsters[i]->mtype].mdlname, monsters[i]->mtype == 5,
|
||||
monstertypes[monsters[i]->mtype].mscale / 10.0f);
|
||||
for (DynamicEntity *monster in monsters)
|
||||
renderclient(monster, false,
|
||||
monstertypes[monster.mtype].mdlname, monster.mtype == 5,
|
||||
monstertypes[monster.mtype].mscale / 10.0f);
|
||||
}
|
||||
|
|
280
src/physics.mm
280
src/physics.mm
|
@ -6,27 +6,29 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
#import "MapModelInfo.h"
|
||||
|
||||
// collide with player or monster
|
||||
bool
|
||||
plcollide(dynent *d, dynent *o, float &headspace, float &hi,
|
||||
float &lo) // collide with player or monster
|
||||
plcollide(
|
||||
DynamicEntity *d, DynamicEntity *o, float &headspace, float &hi, float &lo)
|
||||
{
|
||||
if (o->state != CS_ALIVE)
|
||||
if (o.state != CS_ALIVE)
|
||||
return true;
|
||||
const float r = o->radius + d->radius;
|
||||
if (fabs(o->o.x - d->o.x) < r && fabs(o->o.y - d->o.y) < r) {
|
||||
if (d->o.z - d->eyeheight < o->o.z - o->eyeheight) {
|
||||
if (o->o.z - o->eyeheight < hi)
|
||||
hi = o->o.z - o->eyeheight - 1;
|
||||
} else if (o->o.z + o->aboveeye > lo)
|
||||
lo = o->o.z + o->aboveeye + 1;
|
||||
const float r = o.radius + d.radius;
|
||||
if (fabs(o.o.x - d.o.x) < r && fabs(o.o.y - d.o.y) < r) {
|
||||
if (d.o.z - d.eyeheight < o.o.z - o.eyeheight) {
|
||||
if (o.o.z - o.eyeheight < hi)
|
||||
hi = o.o.z - o.eyeheight - 1;
|
||||
} else if (o.o.z + o.aboveeye > lo)
|
||||
lo = o.o.z + o.aboveeye + 1;
|
||||
|
||||
if (fabs(o->o.z - d->o.z) < o->aboveeye + d->eyeheight)
|
||||
if (fabs(o.o.z - d.o.z) < o.aboveeye + d.eyeheight)
|
||||
return false;
|
||||
if (d->monsterstate)
|
||||
if (d.monsterstate)
|
||||
return false; // hack
|
||||
headspace = d->o.z - o->o.z - o->aboveeye - d->eyeheight;
|
||||
headspace = d.o.z - o.o.z - o.aboveeye - d.eyeheight;
|
||||
if (headspace < 0)
|
||||
headspace = 10;
|
||||
}
|
||||
|
@ -54,7 +56,7 @@ cornertest(int mip, int x, int y, int dx, int dy, int &bx, int &by,
|
|||
}
|
||||
|
||||
void
|
||||
mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
|
||||
mmcollide(DynamicEntity *d, float &hi, float &lo) // collide with a mapmodel
|
||||
{
|
||||
loopv(ents)
|
||||
{
|
||||
|
@ -64,11 +66,11 @@ mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
|
|||
MapModelInfo *mmi = getmminfo(e.attr2);
|
||||
if (mmi == nil || !mmi.h)
|
||||
continue;
|
||||
const float r = mmi.rad + d->radius;
|
||||
if (fabs(e.x - d->o.x) < r && fabs(e.y - d->o.y) < r) {
|
||||
const float r = mmi.rad + d.radius;
|
||||
if (fabs(e.x - d.o.x) < r && fabs(e.y - d.o.y) < r) {
|
||||
float mmz =
|
||||
(float)(S(e.x, e.y)->floor + mmi.zoff + e.attr3);
|
||||
if (d->o.z - d->eyeheight < mmz) {
|
||||
if (d.o.z - d.eyeheight < mmz) {
|
||||
if (mmz < hi)
|
||||
hi = mmz;
|
||||
} else if (mmz + mmi.h > lo)
|
||||
|
@ -83,27 +85,26 @@ mmcollide(dynent *d, float &hi, float &lo) // collide with a mapmodel
|
|||
// current mini-timestep
|
||||
|
||||
bool
|
||||
collide(dynent *d, bool spawn, float drop, float rise)
|
||||
collide(DynamicEntity *d, bool spawn, float drop, float rise)
|
||||
{
|
||||
const float fx1 =
|
||||
d->o.x - d->radius; // figure out integer cube rectangle this entity
|
||||
// covers in map
|
||||
const float fy1 = d->o.y - d->radius;
|
||||
const float fx2 = d->o.x + d->radius;
|
||||
const float fy2 = d->o.y + d->radius;
|
||||
// figure out integer cube rectangle this entity covers in map
|
||||
const float fx1 = d.o.x - d.radius;
|
||||
const float fy1 = d.o.y - d.radius;
|
||||
const float fx2 = d.o.x + d.radius;
|
||||
const float fy2 = d.o.y + d.radius;
|
||||
const int x1 = fast_f2nat(fx1);
|
||||
const int y1 = fast_f2nat(fy1);
|
||||
const int x2 = fast_f2nat(fx2);
|
||||
const int y2 = fast_f2nat(fy2);
|
||||
float hi = 127, lo = -128;
|
||||
float minfloor = (d->monsterstate && !spawn && d->health > 100)
|
||||
? d->o.z - d->eyeheight - 4.5f
|
||||
: -1000.0f; // big monsters are afraid of heights,
|
||||
// unless angry :)
|
||||
// big monsters are afraid of heights, unless angry :)
|
||||
float minfloor = (d.monsterstate && !spawn && d.health > 100)
|
||||
? d.o.z - d.eyeheight - 4.5f
|
||||
: -1000.0f;
|
||||
|
||||
for (int x = x1; x <= x2; x++)
|
||||
for (int y = y1; y <= y2; y++) // collide with map
|
||||
{
|
||||
for (int y = y1; y <= y2; y++) {
|
||||
// collide with map
|
||||
if (OUTBORD(x, y))
|
||||
return false;
|
||||
sqr *s = S(x, y);
|
||||
|
@ -156,55 +157,62 @@ collide(dynent *d, bool spawn, float drop, float rise)
|
|||
return false;
|
||||
}
|
||||
|
||||
if (hi - lo < d->eyeheight + d->aboveeye)
|
||||
if (hi - lo < d.eyeheight + d.aboveeye)
|
||||
return false;
|
||||
|
||||
float headspace = 10;
|
||||
loopv(players) // collide with other players
|
||||
{
|
||||
dynent *o = players[i];
|
||||
if (!o || o == d)
|
||||
for (id player in players) {
|
||||
if (player == [OFNull null] || player == d)
|
||||
continue;
|
||||
if (!plcollide(d, o, headspace, hi, lo))
|
||||
if (!plcollide(d, player, headspace, hi, lo))
|
||||
return false;
|
||||
}
|
||||
if (d != player1)
|
||||
if (!plcollide(d, player1, headspace, hi, lo))
|
||||
return false;
|
||||
dvector &v = getmonsters();
|
||||
// this loop can be a performance bottleneck with many monster on a slow
|
||||
// cpu, should replace with a blockmap but seems mostly fast enough
|
||||
loopv(v) if (!vreject(d->o, v[i]->o, 7.0f) && d != v[i] &&
|
||||
!plcollide(d, v[i], headspace, hi, lo)) return false;
|
||||
for (DynamicEntity *monster in getmonsters())
|
||||
if (!vreject(d.o, monster.o, 7.0f) && d != monster &&
|
||||
!plcollide(d, monster, headspace, hi, lo))
|
||||
return false;
|
||||
headspace -= 0.01f;
|
||||
|
||||
mmcollide(d, hi, lo); // collide with map models
|
||||
|
||||
if (spawn) {
|
||||
d->o.z = lo + d->eyeheight; // just drop to floor (sideeffect)
|
||||
d->onfloor = true;
|
||||
// just drop to floor (sideeffect)
|
||||
d.o = OFMakeVector3D(d.o.x, d.o.y, lo + d.eyeheight);
|
||||
d.onfloor = true;
|
||||
} else {
|
||||
const float space = d->o.z - d->eyeheight - lo;
|
||||
const float space = d.o.z - d.eyeheight - lo;
|
||||
if (space < 0) {
|
||||
if (space > -0.01)
|
||||
d->o.z = lo + d->eyeheight; // stick on step
|
||||
// stick on step
|
||||
d.o = OFMakeVector3D(
|
||||
d.o.x, d.o.y, lo + d.eyeheight);
|
||||
else if (space > -1.26f)
|
||||
d->o.z += rise; // rise thru stair
|
||||
// rise thru stair
|
||||
d.o =
|
||||
OFMakeVector3D(d.o.x, d.o.y, d.o.z + rise);
|
||||
else
|
||||
return false;
|
||||
} else {
|
||||
d->o.z -= min(min(drop, space), headspace); // gravity
|
||||
}
|
||||
} else
|
||||
// gravity
|
||||
d.o = OFMakeVector3D(d.o.x, d.o.y,
|
||||
d.o.z - min(min(drop, space), headspace));
|
||||
|
||||
const float space2 = hi - (d->o.z + d->aboveeye);
|
||||
const float space2 = hi - (d.o.z + d.aboveeye);
|
||||
if (space2 < 0) {
|
||||
if (space2 < -0.1)
|
||||
return false; // hack alert!
|
||||
d->o.z = hi - d->aboveeye; // glue to ceiling
|
||||
d->vel.z = 0; // cancel out jumping velocity
|
||||
// glue to ceiling
|
||||
d.o = OFMakeVector3D(d.o.x, d.o.y, hi - d.aboveeye);
|
||||
// cancel out jumping velocity
|
||||
d.vel = OFMakeVector3D(d.vel.x, d.vel.y, 0);
|
||||
}
|
||||
|
||||
d->onfloor = d->o.z - d->eyeheight - lo < 0.001f;
|
||||
d.onfloor = d.o.z - d.eyeheight - lo < 0.001f;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -237,125 +245,126 @@ physicsframe() // optimally schedule physics frames inside the graphics frames
|
|||
// multiplayer prediction) local is false for multiplayer prediction
|
||||
|
||||
void
|
||||
moveplayer(dynent *pl, int moveres, bool local, int curtime)
|
||||
moveplayer(DynamicEntity *pl, int moveres, bool local, int curtime)
|
||||
{
|
||||
const bool water = hdr.waterlevel > pl->o.z - 0.5f;
|
||||
const bool floating = (editmode && local) || pl->state == CS_EDITING;
|
||||
const bool water = hdr.waterlevel > pl.o.z - 0.5f;
|
||||
const bool floating = (editmode && local) || pl.state == CS_EDITING;
|
||||
|
||||
OFVector3D d; // vector of direction we ideally want to move in
|
||||
|
||||
d.x = (float)(pl->move * cos(rad(pl->yaw - 90)));
|
||||
d.y = (float)(pl->move * sin(rad(pl->yaw - 90)));
|
||||
d.x = (float)(pl.move * cos(rad(pl.yaw - 90)));
|
||||
d.y = (float)(pl.move * sin(rad(pl.yaw - 90)));
|
||||
d.z = 0;
|
||||
|
||||
if (floating || water) {
|
||||
d.x *= (float)cos(rad(pl->pitch));
|
||||
d.y *= (float)cos(rad(pl->pitch));
|
||||
d.z = (float)(pl->move * sin(rad(pl->pitch)));
|
||||
d.x *= (float)cos(rad(pl.pitch));
|
||||
d.y *= (float)cos(rad(pl.pitch));
|
||||
d.z = (float)(pl.move * sin(rad(pl.pitch)));
|
||||
}
|
||||
|
||||
d.x += (float)(pl->strafe * cos(rad(pl->yaw - 180)));
|
||||
d.y += (float)(pl->strafe * sin(rad(pl->yaw - 180)));
|
||||
d.x += (float)(pl.strafe * cos(rad(pl.yaw - 180)));
|
||||
d.y += (float)(pl.strafe * sin(rad(pl.yaw - 180)));
|
||||
|
||||
const float speed =
|
||||
curtime / (water ? 2000.0f : 1000.0f) * pl->maxspeed;
|
||||
const float speed = curtime / (water ? 2000.0f : 1000.0f) * pl.maxspeed;
|
||||
const float friction =
|
||||
water ? 20.0f : (pl->onfloor || floating ? 6.0f : 30.0f);
|
||||
water ? 20.0f : (pl.onfloor || floating ? 6.0f : 30.0f);
|
||||
|
||||
const float fpsfric = friction / curtime * 20.0f;
|
||||
|
||||
vmul(pl->vel, fpsfric - 1); // slowly apply friction and direction to
|
||||
// slowly apply friction and direction to
|
||||
// velocity, gives a smooth movement
|
||||
vadd(pl->vel, d);
|
||||
vdiv(pl->vel, fpsfric);
|
||||
d = pl->vel;
|
||||
vmul(pl.vel, fpsfric - 1);
|
||||
vadd(pl.vel, d);
|
||||
vdiv(pl.vel, fpsfric);
|
||||
d = pl.vel;
|
||||
vmul(d, speed); // d is now frametime based velocity vector
|
||||
|
||||
pl->blocked = false;
|
||||
pl->moving = true;
|
||||
pl.blocked = false;
|
||||
pl.moving = true;
|
||||
|
||||
if (floating) // just apply velocity
|
||||
{
|
||||
vadd(pl->o, d);
|
||||
if (pl->jumpnext) {
|
||||
pl->jumpnext = false;
|
||||
pl->vel.z = 2;
|
||||
if (floating) {
|
||||
// just apply velocity
|
||||
vadd(pl.o, d);
|
||||
if (pl.jumpnext) {
|
||||
pl.jumpnext = false;
|
||||
pl.vel = OFMakeVector3D(pl.vel.x, pl.vel.y, 2);
|
||||
}
|
||||
} else // apply velocity with collision
|
||||
{
|
||||
if (pl->onfloor || water) {
|
||||
if (pl->jumpnext) {
|
||||
pl->jumpnext = false;
|
||||
pl->vel.z = 1.7f; // physics impulse upwards
|
||||
} else {
|
||||
// apply velocity with collision
|
||||
if (pl.onfloor || water) {
|
||||
if (pl.jumpnext) {
|
||||
pl.jumpnext = false;
|
||||
// physics impulse upwards
|
||||
pl.vel =
|
||||
OFMakeVector3D(pl.vel.x, pl.vel.y, 1.7);
|
||||
// dampen velocity change even harder, gives
|
||||
// correct water feel
|
||||
if (water) {
|
||||
pl->vel.x /= 8;
|
||||
pl->vel.y /= 8;
|
||||
}
|
||||
if (water)
|
||||
pl.vel = OFMakeVector3D(pl.vel.x / 8,
|
||||
pl.vel.y / 8, pl.vel.z);
|
||||
if (local)
|
||||
playsoundc(S_JUMP);
|
||||
else if (pl->monsterstate)
|
||||
playsound(S_JUMP, &pl->o);
|
||||
} else if (pl->timeinair > 800) {
|
||||
else if (pl.monsterstate) {
|
||||
OFVector3D loc = pl.o;
|
||||
playsound(S_JUMP, &loc);
|
||||
}
|
||||
} else if (pl.timeinair > 800) {
|
||||
// if we land after long time must have been a
|
||||
// high jump, make thud sound
|
||||
if (local)
|
||||
playsoundc(S_LAND);
|
||||
else if (pl->monsterstate)
|
||||
playsound(S_LAND, &pl->o);
|
||||
else if (pl.monsterstate) {
|
||||
OFVector3D loc = pl.o;
|
||||
playsound(S_LAND, &loc);
|
||||
}
|
||||
pl->timeinair = 0;
|
||||
} else {
|
||||
pl->timeinair += curtime;
|
||||
}
|
||||
pl.timeinair = 0;
|
||||
} else
|
||||
pl.timeinair += curtime;
|
||||
|
||||
const float gravity = 20;
|
||||
const float f = 1.0f / moveres;
|
||||
// incorrect, but works fine
|
||||
float dropf = ((gravity - 1) + pl->timeinair / 15.0f);
|
||||
float dropf = ((gravity - 1) + pl.timeinair / 15.0f);
|
||||
// float slowly down in water
|
||||
if (water) {
|
||||
dropf = 5;
|
||||
pl->timeinair = 0;
|
||||
pl.timeinair = 0;
|
||||
}
|
||||
const float drop = dropf * curtime / gravity / 100 /
|
||||
moveres; // at high fps, gravity kicks in too fast
|
||||
const float rise = speed / moveres /
|
||||
1.2f; // extra smoothness when lifting up stairs
|
||||
// at high fps, gravity kicks in too fast
|
||||
const float drop = dropf * curtime / gravity / 100 / moveres;
|
||||
// extra smoothness when lifting up stairs
|
||||
const float rise = speed / moveres / 1.2f;
|
||||
|
||||
loopi(moveres) // discrete steps collision detection & sliding
|
||||
{
|
||||
// try move forward
|
||||
pl->o.x += f * d.x;
|
||||
pl->o.y += f * d.y;
|
||||
pl->o.z += f * d.z;
|
||||
pl.o = OFMakeVector3D(pl.o.x + f * d.x,
|
||||
pl.o.y + f * d.y, pl.o.z + f * d.z);
|
||||
if (collide(pl, false, drop, rise))
|
||||
continue;
|
||||
// player stuck, try slide along y axis
|
||||
pl->blocked = true;
|
||||
pl->o.x -= f * d.x;
|
||||
pl.blocked = true;
|
||||
pl.o = OFMakeVector3D(pl.o.x - f * d.x, pl.o.y, pl.o.z);
|
||||
if (collide(pl, false, drop, rise)) {
|
||||
d.x = 0;
|
||||
continue;
|
||||
}
|
||||
pl->o.x += f * d.x;
|
||||
// still stuck, try x axis
|
||||
pl->o.y -= f * d.y;
|
||||
pl.o = OFMakeVector3D(
|
||||
pl.o.x + f * d.x, pl.o.y - f * d.y, pl.o.z);
|
||||
if (collide(pl, false, drop, rise)) {
|
||||
d.y = 0;
|
||||
continue;
|
||||
}
|
||||
pl->o.y += f * d.y;
|
||||
// try just dropping down
|
||||
pl->moving = false;
|
||||
pl->o.x -= f * d.x;
|
||||
pl->o.y -= f * d.y;
|
||||
pl.moving = false;
|
||||
pl.o = OFMakeVector3D(pl.o.x - f * d.x, pl.o.y, pl.o.z);
|
||||
if (collide(pl, false, drop, rise)) {
|
||||
d.y = d.x = 0;
|
||||
continue;
|
||||
}
|
||||
pl->o.z -= f * d.z;
|
||||
pl.o = OFMakeVector3D(pl.o.x, pl.o.y, pl.o.z - f * d.z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -363,39 +372,42 @@ moveplayer(dynent *pl, int moveres, bool local, int curtime)
|
|||
// detect wether player is outside map, used for skipping zbuffer clear
|
||||
// mostly
|
||||
|
||||
if (pl->o.x < 0 || pl->o.x >= ssize || pl->o.y < 0 || pl->o.y > ssize) {
|
||||
pl->outsidemap = true;
|
||||
} else {
|
||||
sqr *s = S((int)pl->o.x, (int)pl->o.y);
|
||||
pl->outsidemap = SOLID(s) ||
|
||||
pl->o.z < s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
|
||||
pl->o.z > s->ceil + (s->type == CHF ? s->vdelta / 4 : 0);
|
||||
if (pl.o.x < 0 || pl.o.x >= ssize || pl.o.y < 0 || pl.o.y > ssize)
|
||||
pl.outsidemap = true;
|
||||
else {
|
||||
sqr *s = S((int)pl.o.x, (int)pl.o.y);
|
||||
pl.outsidemap = SOLID(s) ||
|
||||
pl.o.z < s->floor - (s->type == FHF ? s->vdelta / 4 : 0) ||
|
||||
pl.o.z > s->ceil + (s->type == CHF ? s->vdelta / 4 : 0);
|
||||
}
|
||||
|
||||
// automatically apply smooth roll when strafing
|
||||
|
||||
if (pl->strafe == 0) {
|
||||
pl->roll = pl->roll / (1 + (float)sqrt((float)curtime) / 25);
|
||||
} else {
|
||||
pl->roll += pl->strafe * curtime / -30.0f;
|
||||
if (pl->roll > maxroll)
|
||||
pl->roll = (float)maxroll;
|
||||
if (pl->roll < -maxroll)
|
||||
pl->roll = (float)-maxroll;
|
||||
if (pl.strafe == 0)
|
||||
pl.roll = pl.roll / (1 + (float)sqrt((float)curtime) / 25);
|
||||
else {
|
||||
pl.roll += pl.strafe * curtime / -30.0f;
|
||||
if (pl.roll > maxroll)
|
||||
pl.roll = (float)maxroll;
|
||||
if (pl.roll < -maxroll)
|
||||
pl.roll = (float)-maxroll;
|
||||
}
|
||||
|
||||
// play sounds on water transitions
|
||||
|
||||
if (!pl->inwater && water) {
|
||||
playsound(S_SPLASH2, &pl->o);
|
||||
pl->vel.z = 0;
|
||||
} else if (pl->inwater && !water)
|
||||
playsound(S_SPLASH1, &pl->o);
|
||||
pl->inwater = water;
|
||||
if (!pl.inwater && water) {
|
||||
OFVector3D loc = pl.o;
|
||||
playsound(S_SPLASH2, &loc);
|
||||
pl.vel = OFMakeVector3D(pl.vel.x, pl.vel.y, 0);
|
||||
} else if (pl.inwater && !water) {
|
||||
OFVector3D loc = pl.o;
|
||||
playsound(S_SPLASH1, &loc);
|
||||
}
|
||||
pl.inwater = water;
|
||||
}
|
||||
|
||||
void
|
||||
moveplayer(dynent *pl, int moveres, bool local)
|
||||
moveplayer(DynamicEntity *pl, int moveres, bool local)
|
||||
{
|
||||
loopi(physicsrepeat) moveplayer(pl, moveres, local,
|
||||
i ? curtime / physicsrepeat
|
||||
|
|
41
src/protos.h
41
src/protos.h
|
@ -78,7 +78,7 @@ extern bool multiplayer();
|
|||
extern bool allowedittoggle();
|
||||
extern void sendpackettoserv(void *packet);
|
||||
extern void gets2c();
|
||||
extern void c2sinfo(dynent *d);
|
||||
extern void c2sinfo(DynamicEntity *d);
|
||||
extern void neterr(OFString *s);
|
||||
extern void initclientnet();
|
||||
extern bool netmapstart();
|
||||
|
@ -87,26 +87,27 @@ extern void changemapserv(OFString *name, int mode);
|
|||
extern void writeclientinfo(OFStream *stream);
|
||||
|
||||
// clientgame
|
||||
extern void initPlayers();
|
||||
extern void mousemove(int dx, int dy);
|
||||
extern void updateworld(int millis);
|
||||
extern void startmap(OFString *name);
|
||||
extern void changemap(OFString *name);
|
||||
extern void initclient();
|
||||
extern void spawnplayer(dynent *d);
|
||||
extern void selfdamage(int damage, int actor, dynent *act);
|
||||
extern dynent *newdynent();
|
||||
extern void spawnplayer(DynamicEntity *d);
|
||||
extern void selfdamage(int damage, int actor, DynamicEntity *act);
|
||||
extern DynamicEntity *newdynent();
|
||||
extern OFString *getclientmap();
|
||||
extern OFString *modestr(int n);
|
||||
extern void zapdynent(dynent *&d);
|
||||
extern dynent *getclient(int cn);
|
||||
extern DynamicEntity *getclient(int cn);
|
||||
extern void setclient(int cn, id client);
|
||||
extern void timeupdate(int timeremain);
|
||||
extern void resetmovement(dynent *d);
|
||||
extern void resetmovement(DynamicEntity *d);
|
||||
extern void fixplayer1range();
|
||||
|
||||
// clientextras
|
||||
extern void renderclients();
|
||||
extern void renderclient(
|
||||
dynent *d, bool team, OFString *mdlname, bool hellpig, float scale);
|
||||
DynamicEntity *d, bool team, OFString *mdlname, bool hellpig, float scale);
|
||||
void showscores(bool on);
|
||||
extern void renderscores();
|
||||
|
||||
|
@ -126,7 +127,7 @@ extern entity *newentity(
|
|||
// worldlight
|
||||
extern void calclight();
|
||||
extern void dodynlight(const OFVector3D &vold, const OFVector3D &v, int reach,
|
||||
int strength, dynent *owner);
|
||||
int strength, DynamicEntity *owner);
|
||||
extern void cleardlights();
|
||||
extern block *blockcopy(block &b);
|
||||
extern void blockpaste(block &b);
|
||||
|
@ -191,13 +192,13 @@ extern void incomingdemodata(uchar *buf, int len, bool extras = false);
|
|||
extern void demoplaybackstep();
|
||||
extern void stop();
|
||||
extern void stopifrecording();
|
||||
extern void demodamage(int damage, OFVector3D &o);
|
||||
extern void demodamage(int damage, const OFVector3D &o);
|
||||
extern void demoblend(int damage);
|
||||
|
||||
// physics
|
||||
extern void moveplayer(dynent *pl, int moveres, bool local);
|
||||
extern bool collide(dynent *d, bool spawn, float drop, float rise);
|
||||
extern void entinmap(dynent *d);
|
||||
extern void moveplayer(DynamicEntity *pl, int moveres, bool local);
|
||||
extern bool collide(DynamicEntity *d, bool spawn, float drop, float rise);
|
||||
extern void entinmap(DynamicEntity *d);
|
||||
extern void setentphysics(int mml, int mmr);
|
||||
extern void physicsframe();
|
||||
|
||||
|
@ -237,9 +238,9 @@ extern ENetPacket *recvmap(int n);
|
|||
|
||||
// weapon
|
||||
extern void selectgun(int a = -1, int b = -1, int c = -1);
|
||||
extern void shoot(dynent *d, OFVector3D &to);
|
||||
extern void shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d = 0,
|
||||
bool local = false);
|
||||
extern void shoot(DynamicEntity *d, const OFVector3D &to);
|
||||
extern void shootv(int gun, OFVector3D &from, OFVector3D &to,
|
||||
DynamicEntity *d = 0, bool local = false);
|
||||
extern void createrays(OFVector3D &from, OFVector3D &to);
|
||||
extern void moveprojectiles(float time);
|
||||
extern void projreset();
|
||||
|
@ -251,8 +252,8 @@ extern void monsterclear();
|
|||
extern void restoremonsterstate();
|
||||
extern void monsterthink();
|
||||
extern void monsterrender();
|
||||
extern dvector &getmonsters();
|
||||
extern void monsterpain(dynent *m, int damage, dynent *d);
|
||||
extern OFArray<DynamicEntity *> *getmonsters();
|
||||
extern void monsterpain(DynamicEntity *m, int damage, DynamicEntity *d);
|
||||
extern void endsp(bool allkilled);
|
||||
|
||||
// entities
|
||||
|
@ -260,11 +261,11 @@ extern void renderents();
|
|||
extern void putitems(uchar *&p);
|
||||
extern void checkquad(int time);
|
||||
extern void checkitems();
|
||||
extern void realpickup(int n, dynent *d);
|
||||
extern void realpickup(int n, DynamicEntity *d);
|
||||
extern void renderentities();
|
||||
extern void resetspawns();
|
||||
extern void setspawn(uint i, bool on);
|
||||
extern void teleport(int n, dynent *d);
|
||||
extern void teleport(int n, DynamicEntity *d);
|
||||
extern void baseammo(int gun);
|
||||
|
||||
// rndmap
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
void
|
||||
line(int x1, int y1, float z1, int x2, int y2, float z2)
|
||||
{
|
||||
|
@ -329,7 +331,7 @@ gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater)
|
|||
readmatrices();
|
||||
if (editmode) {
|
||||
if (cursordepth == 1.0f)
|
||||
worldpos = player1->o;
|
||||
worldpos = player1.o;
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
|
||||
cursorupdate();
|
||||
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
|
||||
|
@ -381,11 +383,11 @@ gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater)
|
|||
glBegin(GL_QUADS);
|
||||
glColor3ub(255, 255, 255);
|
||||
if (crosshairfx) {
|
||||
if (player1->gunwait)
|
||||
if (player1.gunwait)
|
||||
glColor3ub(128, 128, 128);
|
||||
else if (player1->health <= 25)
|
||||
else if (player1.health <= 25)
|
||||
glColor3ub(255, 0, 0);
|
||||
else if (player1->health <= 50)
|
||||
else if (player1.health <= 50)
|
||||
glColor3ub(255, 128, 0);
|
||||
}
|
||||
float chsize = (float)crosshairsize;
|
||||
|
@ -418,23 +420,22 @@ gl_drawhud(int w, int h, int curfps, int nquads, int curvert, bool underwater)
|
|||
|
||||
glPopMatrix();
|
||||
|
||||
if (player1->state == CS_ALIVE) {
|
||||
if (player1.state == CS_ALIVE) {
|
||||
glPushMatrix();
|
||||
glOrtho(0, VIRTW / 2, VIRTH / 2, 0, -1, 1);
|
||||
draw_textf(@"%d", 90, 827, 2, player1->health);
|
||||
if (player1->armour)
|
||||
draw_textf(@"%d", 390, 827, 2, player1->armour);
|
||||
draw_textf(
|
||||
@"%d", 690, 827, 2, player1->ammo[player1->gunselect]);
|
||||
draw_textf(@"%d", 90, 827, 2, player1.health);
|
||||
if (player1.armour)
|
||||
draw_textf(@"%d", 390, 827, 2, player1.armour);
|
||||
draw_textf(@"%d", 690, 827, 2, player1.ammo[player1.gunselect]);
|
||||
glPopMatrix();
|
||||
glPushMatrix();
|
||||
glOrtho(0, VIRTW, VIRTH, 0, -1, 1);
|
||||
glDisable(GL_BLEND);
|
||||
drawicon(128, 128, 20, 1650);
|
||||
if (player1->armour)
|
||||
if (player1.armour)
|
||||
drawicon(
|
||||
(float)(player1->armourtype * 64), 0, 620, 1650);
|
||||
int g = player1->gunselect;
|
||||
(float)(player1.armourtype * 64), 0, 620, 1650);
|
||||
int g = player1.gunselect;
|
||||
int r = 64;
|
||||
if (g > 2) {
|
||||
g -= 3;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
#ifdef DARWIN
|
||||
# define GL_COMBINE_EXT GL_COMBINE_ARB
|
||||
# define GL_COMBINE_RGB_EXT GL_COMBINE_RGB_ARB
|
||||
|
@ -336,14 +338,14 @@ transplayer()
|
|||
{
|
||||
glLoadIdentity();
|
||||
|
||||
glRotated(player1->roll, 0.0, 0.0, 1.0);
|
||||
glRotated(player1->pitch, -1.0, 0.0, 0.0);
|
||||
glRotated(player1->yaw, 0.0, 1.0, 0.0);
|
||||
glRotated(player1.roll, 0.0, 0.0, 1.0);
|
||||
glRotated(player1.pitch, -1.0, 0.0, 0.0);
|
||||
glRotated(player1.yaw, 0.0, 1.0, 0.0);
|
||||
|
||||
glTranslated(-player1->o.x,
|
||||
(player1->state == CS_DEAD ? player1->eyeheight - 0.2f : 0) -
|
||||
player1->o.z,
|
||||
-player1->o.y);
|
||||
glTranslated(-player1.o.x,
|
||||
(player1.state == CS_DEAD ? player1.eyeheight - 0.2f : 0) -
|
||||
player1.o.z,
|
||||
-player1.o.y);
|
||||
}
|
||||
|
||||
VARP(fov, 10, 105, 120);
|
||||
|
@ -361,15 +363,15 @@ OFString *hudgunnames[] = { @"hudguns/fist", @"hudguns/shotg",
|
|||
void
|
||||
drawhudmodel(int start, int end, float speed, int base)
|
||||
{
|
||||
rendermodel(hudgunnames[player1->gunselect], start, end, 0, 1.0f,
|
||||
player1->o.x, player1->o.z, player1->o.y, player1->yaw + 90,
|
||||
player1->pitch, false, 1.0f, speed, 0, base);
|
||||
rendermodel(hudgunnames[player1.gunselect], start, end, 0, 1.0f,
|
||||
player1.o.x, player1.o.z, player1.o.y, player1.yaw + 90,
|
||||
player1.pitch, false, 1.0f, speed, 0, base);
|
||||
}
|
||||
|
||||
void
|
||||
drawhudgun(float fovy, float aspect, int farplane)
|
||||
{
|
||||
if (!hudgun /*|| !player1->gunselect*/)
|
||||
if (!hudgun /*|| !player1.gunselect*/)
|
||||
return;
|
||||
|
||||
glEnable(GL_CULL_FACE);
|
||||
|
@ -380,14 +382,12 @@ drawhudgun(float fovy, float aspect, int farplane)
|
|||
glMatrixMode(GL_MODELVIEW);
|
||||
|
||||
// glClear(GL_DEPTH_BUFFER_BIT);
|
||||
int rtime = reloadtime(player1->gunselect);
|
||||
if (player1->lastaction &&
|
||||
player1->lastattackgun == player1->gunselect &&
|
||||
lastmillis - player1->lastaction < rtime) {
|
||||
drawhudmodel(7, 18, rtime / 18.0f, player1->lastaction);
|
||||
} else {
|
||||
int rtime = reloadtime(player1.gunselect);
|
||||
if (player1.lastaction && player1.lastattackgun == player1.gunselect &&
|
||||
lastmillis - player1.lastaction < rtime) {
|
||||
drawhudmodel(7, 18, rtime / 18.0f, player1.lastaction);
|
||||
} else
|
||||
drawhudmodel(6, 1, 100, 0);
|
||||
}
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
glLoadIdentity();
|
||||
|
@ -403,7 +403,7 @@ gl_drawframe(int w, int h, float curfps)
|
|||
float hf = hdr.waterlevel - 0.3f;
|
||||
float fovy = (float)fov * h / w;
|
||||
float aspect = w / (float)h;
|
||||
bool underwater = player1->o.z < hf;
|
||||
bool underwater = player1.o.z < hf;
|
||||
|
||||
glFogi(GL_FOG_START, (fog + 64) / 8);
|
||||
glFogi(GL_FOG_END, fog);
|
||||
|
@ -420,7 +420,7 @@ gl_drawframe(int w, int h, float curfps)
|
|||
glFogi(GL_FOG_END, (fog + 96) / 8);
|
||||
}
|
||||
|
||||
glClear((player1->outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
|
||||
glClear((player1.outsidemap ? GL_COLOR_BUFFER_BIT : 0) |
|
||||
GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
glMatrixMode(GL_PROJECTION);
|
||||
|
@ -441,8 +441,8 @@ gl_drawframe(int w, int h, float curfps)
|
|||
curvert = 0;
|
||||
strips.setsize(0);
|
||||
|
||||
render_world(player1->o.x, player1->o.y, player1->o.z,
|
||||
(int)player1->yaw, (int)player1->pitch, (float)fov, w, h);
|
||||
render_world(player1.o.x, player1.o.y, player1.o.z, (int)player1.yaw,
|
||||
(int)player1.pitch, (float)fov, w, h);
|
||||
finishstrips();
|
||||
|
||||
setupworld();
|
||||
|
@ -450,8 +450,8 @@ gl_drawframe(int w, int h, float curfps)
|
|||
renderstripssky();
|
||||
|
||||
glLoadIdentity();
|
||||
glRotated(player1->pitch, -1.0, 0.0, 0.0);
|
||||
glRotated(player1->yaw, 0.0, 1.0, 0.0);
|
||||
glRotated(player1.pitch, -1.0, 0.0, 0.0);
|
||||
glRotated(player1.yaw, 0.0, 1.0, 0.0);
|
||||
glRotated(90.0, 1.0, 0.0, 0.0);
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
glDisable(GL_FOG);
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
#import "MD2.h"
|
||||
#import "MapModelInfo.h"
|
||||
|
||||
|
@ -100,7 +101,7 @@ rendermodel(OFString *mdl, int frame, int range, int tex, float rad, float x,
|
|||
{
|
||||
MD2 *m = loadmodel(mdl);
|
||||
|
||||
if (isoccluded(player1->o.x, player1->o.y, x - rad, z - rad, rad * 2))
|
||||
if (isoccluded(player1.o.x, player1.o.y, x - rad, z - rad, rad * 2))
|
||||
return;
|
||||
|
||||
delayedload(m);
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
const int MAXPARTICLES = 10500;
|
||||
const int NUMPARTCUTOFF = 20;
|
||||
struct particle {
|
||||
|
@ -56,7 +58,7 @@ render_particles(int time)
|
|||
{
|
||||
if (demoplayback && demotracking) {
|
||||
OFVector3D nom = OFMakeVector3D(0, 0, 0);
|
||||
newparticle(player1->o, nom, 100000000, 8);
|
||||
newparticle(player1.o, nom, 100000000, 8);
|
||||
}
|
||||
|
||||
glDepthMask(GL_FALSE);
|
||||
|
|
|
@ -3,17 +3,19 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
#ifdef OF_BIG_ENDIAN
|
||||
static const int islittleendian = 0;
|
||||
#else
|
||||
static const int islittleendian = 1;
|
||||
#endif
|
||||
|
||||
gzFile f = NULL;
|
||||
static gzFile f = NULL;
|
||||
bool demorecording = false;
|
||||
bool demoplayback = false;
|
||||
bool demoloading = false;
|
||||
dvector playerhistory;
|
||||
static OFMutableArray<DynamicEntity *> *playerhistory;
|
||||
int democlientnum = 0;
|
||||
|
||||
void startdemo();
|
||||
|
@ -76,8 +78,7 @@ stop()
|
|||
demorecording = false;
|
||||
demoplayback = false;
|
||||
demoloading = false;
|
||||
loopv(playerhistory) zapdynent(playerhistory[i]);
|
||||
playerhistory.setsize(0);
|
||||
[playerhistory removeAllObjects];
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -102,20 +103,27 @@ savestate(OFIRI *IRI)
|
|||
gzwrite(f, (void *)"CUBESAVE", 8);
|
||||
gzputc(f, islittleendian);
|
||||
gzputi(SAVEGAMEVERSION);
|
||||
gzputi(sizeof(dynent));
|
||||
gzwrite(f, getclientmap().UTF8String, _MAXDEFSTR);
|
||||
OFData *data = [player1 dataBySerializing];
|
||||
gzputi(data.count);
|
||||
char map[260] = { 0 };
|
||||
memcpy(map, getclientmap().UTF8String,
|
||||
min(getclientmap().UTF8StringLength, 259));
|
||||
gzwrite(f, map, _MAXDEFSTR);
|
||||
gzputi(gamemode);
|
||||
gzputi(ents.length());
|
||||
loopv(ents) gzputc(f, ents[i].spawned);
|
||||
gzwrite(f, player1, sizeof(dynent));
|
||||
dvector &monsters = getmonsters();
|
||||
gzputi(monsters.length());
|
||||
loopv(monsters) gzwrite(f, monsters[i], sizeof(dynent));
|
||||
gzputi(players.length());
|
||||
loopv(players)
|
||||
{
|
||||
gzput(players[i] == NULL);
|
||||
gzwrite(f, players[i], sizeof(dynent));
|
||||
gzwrite(f, data.items, data.count);
|
||||
OFArray<DynamicEntity *> *monsters = getmonsters();
|
||||
gzputi(monsters.count);
|
||||
for (DynamicEntity *monster in monsters) {
|
||||
data = [monster dataBySerializing];
|
||||
gzwrite(f, data.items, data.count);
|
||||
}
|
||||
gzputi(players.count);
|
||||
for (id player in players) {
|
||||
gzput(player == [OFNull null]);
|
||||
data = [player dataBySerializing];
|
||||
gzwrite(f, data.items, data.count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +171,8 @@ loadstate(OFIRI *IRI)
|
|||
goto out; // not supporting save->load accross
|
||||
// incompatible architectures simpifies things
|
||||
// a LOT
|
||||
if (gzgeti() != SAVEGAMEVERSION || gzgeti() != sizeof(dynent))
|
||||
if (gzgeti() != SAVEGAMEVERSION ||
|
||||
gzgeti() != DynamicEntity.serializedSize)
|
||||
goto out;
|
||||
string mapname;
|
||||
gzread(f, mapname, _MAXDEFSTR);
|
||||
|
@ -218,31 +227,37 @@ loadgamerest()
|
|||
}
|
||||
restoreserverstate(ents);
|
||||
|
||||
gzread(f, player1, sizeof(dynent));
|
||||
player1->lastaction = lastmillis;
|
||||
OFMutableData *data =
|
||||
[OFMutableData dataWithCapacity:DynamicEntity.serializedSize];
|
||||
[data increaseCountBy:DynamicEntity.serializedSize];
|
||||
gzread(f, data.mutableItems, data.count);
|
||||
[player1 setFromSerializedData:data];
|
||||
player1.lastaction = lastmillis;
|
||||
|
||||
int nmonsters = gzgeti();
|
||||
dvector &monsters = getmonsters();
|
||||
if (nmonsters != monsters.length())
|
||||
OFArray<DynamicEntity *> *monsters = getmonsters();
|
||||
if (nmonsters != monsters.count)
|
||||
return loadgameout();
|
||||
loopv(monsters)
|
||||
{
|
||||
gzread(f, monsters[i], sizeof(dynent));
|
||||
monsters[i]->enemy =
|
||||
player1; // lazy, could save id of enemy instead
|
||||
monsters[i]->lastaction = monsters[i]->trigger = lastmillis +
|
||||
500; // also lazy, but no real noticable effect on game
|
||||
if (monsters[i]->state == CS_DEAD)
|
||||
monsters[i]->lastaction = 0;
|
||||
|
||||
for (DynamicEntity *monster in monsters) {
|
||||
gzread(f, data.mutableItems, data.count);
|
||||
[monster setFromSerializedData:data];
|
||||
// lazy, could save id of enemy instead
|
||||
monster.enemy = player1;
|
||||
// also lazy, but no real noticable effect on game
|
||||
monster.lastaction = monster.trigger = lastmillis + 500;
|
||||
if (monster.state == CS_DEAD)
|
||||
monster.lastaction = 0;
|
||||
}
|
||||
restoremonsterstate();
|
||||
|
||||
int nplayers = gzgeti();
|
||||
loopi(nplayers) if (!gzget())
|
||||
{
|
||||
dynent *d = getclient(i);
|
||||
DynamicEntity *d = getclient(i);
|
||||
assert(d);
|
||||
gzread(f, d, sizeof(dynent));
|
||||
gzread(f, data.mutableItems, data.count);
|
||||
[d setFromSerializedData:data];
|
||||
}
|
||||
|
||||
conoutf(@"savegame restored");
|
||||
|
@ -287,11 +302,12 @@ record(OFString *name)
|
|||
COMMAND(record, ARG_1STR)
|
||||
|
||||
void
|
||||
demodamage(int damage, OFVector3D &o)
|
||||
demodamage(int damage, const OFVector3D &o)
|
||||
{
|
||||
ddamage = damage;
|
||||
dorig = o;
|
||||
}
|
||||
|
||||
void
|
||||
demoblend(int damage)
|
||||
{
|
||||
|
@ -308,15 +324,15 @@ incomingdemodata(uchar *buf, int len, bool extras)
|
|||
gzwrite(f, buf, len);
|
||||
gzput(extras);
|
||||
if (extras) {
|
||||
gzput(player1->gunselect);
|
||||
gzput(player1->lastattackgun);
|
||||
gzputi(player1->lastaction - starttime);
|
||||
gzputi(player1->gunwait);
|
||||
gzputi(player1->health);
|
||||
gzputi(player1->armour);
|
||||
gzput(player1->armourtype);
|
||||
loopi(NUMGUNS) gzput(player1->ammo[i]);
|
||||
gzput(player1->state);
|
||||
gzput(player1.gunselect);
|
||||
gzput(player1.lastattackgun);
|
||||
gzputi(player1.lastaction - starttime);
|
||||
gzputi(player1.gunwait);
|
||||
gzputi(player1.health);
|
||||
gzputi(player1.armour);
|
||||
gzput(player1.armourtype);
|
||||
loopi(NUMGUNS) gzput(player1.ammo[i]);
|
||||
gzput(player1.state);
|
||||
gzputi(bdamage);
|
||||
bdamage = 0;
|
||||
gzputi(ddamage);
|
||||
|
@ -348,7 +364,7 @@ stopreset()
|
|||
{
|
||||
conoutf(@"demo stopped (%d msec elapsed)", lastmillis - starttime);
|
||||
stop();
|
||||
loopv(players) zapdynent(players[i]);
|
||||
[players removeAllObjects];
|
||||
disconnect(0, 0);
|
||||
}
|
||||
|
||||
|
@ -376,47 +392,44 @@ startdemo()
|
|||
demoplayback = true;
|
||||
starttime = lastmillis;
|
||||
conoutf(@"now playing demo");
|
||||
dynent *d = getclient(democlientnum);
|
||||
assert(d);
|
||||
*d = *player1;
|
||||
setclient(democlientnum, [player1 copy]);
|
||||
readdemotime();
|
||||
}
|
||||
|
||||
VAR(demodelaymsec, 0, 120, 500);
|
||||
|
||||
// spline interpolation
|
||||
void
|
||||
catmulrom(OFVector3D &z, OFVector3D &a, OFVector3D &b, OFVector3D &c, float s,
|
||||
OFVector3D &dest)
|
||||
{
|
||||
OFVector3D t1 = b, t2 = c;
|
||||
|
||||
vsub(t1, z);
|
||||
vmul(t1, 0.5f) vsub(t2, a);
|
||||
vmul(t2, 0.5f);
|
||||
|
||||
float s2 = s * s;
|
||||
float s3 = s * s2;
|
||||
|
||||
dest = a;
|
||||
OFVector3D t = b;
|
||||
|
||||
vmul(dest, 2 * s3 - 3 * s2 + 1);
|
||||
vmul(t, -2 * s3 + 3 * s2);
|
||||
vadd(dest, t);
|
||||
vmul(t1, s3 - 2 * s2 + s);
|
||||
vadd(dest, t1);
|
||||
vmul(t2, s3 - s2);
|
||||
vadd(dest, t2);
|
||||
}
|
||||
#define catmulrom(z, a, b, c, s, dest) \
|
||||
{ \
|
||||
OFVector3D t1 = b, t2 = c; \
|
||||
\
|
||||
vsub(t1, z); \
|
||||
vmul(t1, 0.5f); \
|
||||
vsub(t2, a); \
|
||||
vmul(t2, 0.5f); \
|
||||
\
|
||||
float s2 = s * s; \
|
||||
float s3 = s * s2; \
|
||||
\
|
||||
dest = a; \
|
||||
OFVector3D t = b; \
|
||||
\
|
||||
vmul(dest, 2 * s3 - 3 * s2 + 1); \
|
||||
vmul(t, -2 * s3 + 3 * s2); \
|
||||
vadd(dest, t); \
|
||||
vmul(t1, s3 - 2 * s2 + s); \
|
||||
vadd(dest, t1); \
|
||||
vmul(t2, s3 - s2); \
|
||||
vadd(dest, t2); \
|
||||
}
|
||||
|
||||
void
|
||||
fixwrap(dynent *a, dynent *b)
|
||||
fixwrap(DynamicEntity *a, DynamicEntity *b)
|
||||
{
|
||||
while (b->yaw - a->yaw > 180)
|
||||
a->yaw += 360;
|
||||
while (b->yaw - a->yaw < -180)
|
||||
a->yaw -= 360;
|
||||
while (b.yaw - a.yaw > 180)
|
||||
a.yaw += 360;
|
||||
while (b.yaw - a.yaw < -180)
|
||||
a.yaw -= 360;
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -434,23 +447,23 @@ demoplaybackstep()
|
|||
gzread(f, buf, len);
|
||||
localservertoclient(buf, len); // update game state
|
||||
|
||||
dynent *target = players[democlientnum];
|
||||
DynamicEntity *target = players[democlientnum];
|
||||
assert(target);
|
||||
|
||||
int extras;
|
||||
if (extras = gzget()) // read additional client side state not
|
||||
// present in normal network stream
|
||||
{
|
||||
target->gunselect = gzget();
|
||||
target->lastattackgun = gzget();
|
||||
target->lastaction = scaletime(gzgeti());
|
||||
target->gunwait = gzgeti();
|
||||
target->health = gzgeti();
|
||||
target->armour = gzgeti();
|
||||
target->armourtype = gzget();
|
||||
loopi(NUMGUNS) target->ammo[i] = gzget();
|
||||
target->state = gzget();
|
||||
target->lastmove = playbacktime;
|
||||
// read additional client side state not present in normal
|
||||
// network stream
|
||||
if (extras = gzget()) {
|
||||
target.gunselect = gzget();
|
||||
target.lastattackgun = gzget();
|
||||
target.lastaction = scaletime(gzgeti());
|
||||
target.gunwait = gzgeti();
|
||||
target.health = gzgeti();
|
||||
target.armour = gzgeti();
|
||||
target.armourtype = gzget();
|
||||
loopi(NUMGUNS) target.ammo[i] = gzget();
|
||||
target.state = gzget();
|
||||
target.lastmove = playbacktime;
|
||||
if (bdamage = gzgeti())
|
||||
damageblend(bdamage);
|
||||
if (ddamage = gzgeti()) {
|
||||
|
@ -462,66 +475,87 @@ demoplaybackstep()
|
|||
|
||||
// insert latest copy of player into history
|
||||
if (extras &&
|
||||
(playerhistory.empty() ||
|
||||
playerhistory.last()->lastupdate != playbacktime)) {
|
||||
dynent *d = newdynent();
|
||||
*d = *target;
|
||||
d->lastupdate = playbacktime;
|
||||
playerhistory.add(d);
|
||||
if (playerhistory.length() > 20) {
|
||||
zapdynent(playerhistory[0]);
|
||||
playerhistory.remove(0);
|
||||
}
|
||||
(playerhistory.count == 0 ||
|
||||
playerhistory.lastObject.lastupdate != playbacktime)) {
|
||||
DynamicEntity *d = [target copy];
|
||||
d.lastupdate = playbacktime;
|
||||
[playerhistory addObject:d];
|
||||
if (playerhistory.count > 20)
|
||||
[playerhistory removeObjectAtIndex:0];
|
||||
}
|
||||
|
||||
readdemotime();
|
||||
}
|
||||
|
||||
if (demoplayback) {
|
||||
if (!demoplayback)
|
||||
return;
|
||||
|
||||
int itime = lastmillis - demodelaymsec;
|
||||
loopvrev(playerhistory) if (playerhistory[i]->lastupdate <
|
||||
itime) // find 2 positions in
|
||||
// history that surround
|
||||
// interpolation time point
|
||||
{
|
||||
dynent *a = playerhistory[i];
|
||||
dynent *b = a;
|
||||
if (i + 1 < playerhistory.length())
|
||||
// find 2 positions in history that surround interpolation time point
|
||||
size_t count = playerhistory.count;
|
||||
for (ssize_t i = count - 1; i >= 0; i--) {
|
||||
if (playerhistory[i].lastupdate < itime) {
|
||||
DynamicEntity *a = playerhistory[i];
|
||||
DynamicEntity *b = a;
|
||||
|
||||
if (i + 1 < playerhistory.count)
|
||||
b = playerhistory[i + 1];
|
||||
*player1 = *b;
|
||||
if (a != b) // interpolate pos & angles
|
||||
{
|
||||
dynent *c = b;
|
||||
if (i + 2 < playerhistory.length())
|
||||
|
||||
player1 = b;
|
||||
// interpolate pos & angles
|
||||
if (a != b) {
|
||||
DynamicEntity *c = b;
|
||||
if (i + 2 < playerhistory.count)
|
||||
c = playerhistory[i + 2];
|
||||
dynent *z = a;
|
||||
DynamicEntity *z = a;
|
||||
if (i - 1 >= 0)
|
||||
z = playerhistory[i - 1];
|
||||
// if(a==z || b==c) printf("* %d\n",
|
||||
// lastmillis);
|
||||
float bf = (itime - a->lastupdate) /
|
||||
(float)(b->lastupdate - a->lastupdate);
|
||||
// if(a==z || b==c)
|
||||
// printf("* %d\n", lastmillis);
|
||||
float bf = (itime - a.lastupdate) /
|
||||
(float)(b.lastupdate - a.lastupdate);
|
||||
fixwrap(a, player1);
|
||||
fixwrap(c, player1);
|
||||
fixwrap(z, player1);
|
||||
vdist(dist, v, z->o, c->o);
|
||||
if (dist < 16) // if teleport or spawn, dont't
|
||||
// interpolate
|
||||
{
|
||||
catmulrom(z->o, a->o, b->o, c->o, bf,
|
||||
player1->o);
|
||||
catmulrom(*(OFVector3D *)&z->yaw,
|
||||
*(OFVector3D *)&a->yaw,
|
||||
*(OFVector3D *)&b->yaw,
|
||||
*(OFVector3D *)&c->yaw, bf,
|
||||
*(OFVector3D *)&player1->yaw);
|
||||
vdist(dist, v, z.o, c.o);
|
||||
// if teleport or spawn, don't interpolate
|
||||
if (dist < 16) {
|
||||
catmulrom(
|
||||
z.o, a.o, b.o, c.o, bf, player1.o);
|
||||
OFVector3D vz = OFMakeVector3D(
|
||||
z.yaw, z.pitch, z.roll);
|
||||
OFVector3D va = OFMakeVector3D(
|
||||
a.yaw, a.pitch, a.roll);
|
||||
OFVector3D vb = OFMakeVector3D(
|
||||
b.yaw, b.pitch, b.roll);
|
||||
OFVector3D vc = OFMakeVector3D(
|
||||
c.yaw, c.pitch, c.roll);
|
||||
OFVector3D vp1 =
|
||||
OFMakeVector3D(player1.yaw,
|
||||
player1.pitch, player1.roll);
|
||||
catmulrom(vz, va, vb, vc, bf, vp1);
|
||||
z.yaw = vz.x;
|
||||
z.pitch = vz.y;
|
||||
z.roll = vz.z;
|
||||
a.yaw = va.x;
|
||||
a.pitch = va.y;
|
||||
a.roll = va.z;
|
||||
b.yaw = vb.x;
|
||||
b.pitch = vb.y;
|
||||
b.roll = vb.z;
|
||||
c.yaw = vc.x;
|
||||
c.pitch = vc.y;
|
||||
c.roll = vc.z;
|
||||
player1.yaw = vp1.x;
|
||||
player1.pitch = vp1.y;
|
||||
player1.roll = vp1.z;
|
||||
}
|
||||
fixplayer1range();
|
||||
}
|
||||
break;
|
||||
}
|
||||
// if(player1->state!=CS_DEAD) showscores(false);
|
||||
}
|
||||
// if(player1->state!=CS_DEAD) showscores(false);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
16
src/sound.mm
16
src/sound.mm
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
// #ifndef _WIN32 // NOTE: fmod not being supported for the moment as it does
|
||||
// not allow stereo pan/vol updating during playback
|
||||
#define USE_MIXER
|
||||
|
@ -173,17 +175,15 @@ updatechanvol(int chan, const OFVector3D *loc)
|
|||
{
|
||||
int vol = soundvol, pan = 255 / 2;
|
||||
if (loc) {
|
||||
vdist(dist, v, *loc, player1->o);
|
||||
vdist(dist, v, *loc, player1.o);
|
||||
vol -= (int)(dist * 3 * soundvol /
|
||||
255); // simple mono distance attenuation
|
||||
if (stereo && (v.x != 0 || v.y != 0)) {
|
||||
float yaw = -atan2(v.x, v.y) -
|
||||
player1->yaw *
|
||||
(PI / 180.0f); // relative angle of
|
||||
// sound along X-Y axis
|
||||
pan = int(255.9f *
|
||||
(0.5 * sin(yaw) + 0.5f)); // range is from 0 (left)
|
||||
// to 255 (right)
|
||||
// relative angle of sound along X-Y axis
|
||||
float yaw =
|
||||
-atan2(v.x, v.y) - player1.yaw * (PI / 180.0f);
|
||||
// range is from 0 (left) to 255 (right)
|
||||
pan = int(255.9f * (0.5 * sin(yaw) + 0.5f));
|
||||
}
|
||||
}
|
||||
vol = (vol * MAXVOL) / 255;
|
||||
|
|
218
src/weapon.mm
218
src/weapon.mm
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
#import "Projectile.h"
|
||||
|
||||
static const int MONSTERDAMAGEFACTOR = 4;
|
||||
|
@ -30,26 +31,26 @@ selectgun(int a, int b, int c)
|
|||
if (a < -1 || b < -1 || c < -1 || a >= NUMGUNS || b >= NUMGUNS ||
|
||||
c >= NUMGUNS)
|
||||
return;
|
||||
int s = player1->gunselect;
|
||||
if (a >= 0 && s != a && player1->ammo[a])
|
||||
int s = player1.gunselect;
|
||||
if (a >= 0 && s != a && player1.ammo[a])
|
||||
s = a;
|
||||
else if (b >= 0 && s != b && player1->ammo[b])
|
||||
else if (b >= 0 && s != b && player1.ammo[b])
|
||||
s = b;
|
||||
else if (c >= 0 && s != c && player1->ammo[c])
|
||||
else if (c >= 0 && s != c && player1.ammo[c])
|
||||
s = c;
|
||||
else if (s != GUN_RL && player1->ammo[GUN_RL])
|
||||
else if (s != GUN_RL && player1.ammo[GUN_RL])
|
||||
s = GUN_RL;
|
||||
else if (s != GUN_CG && player1->ammo[GUN_CG])
|
||||
else if (s != GUN_CG && player1.ammo[GUN_CG])
|
||||
s = GUN_CG;
|
||||
else if (s != GUN_SG && player1->ammo[GUN_SG])
|
||||
else if (s != GUN_SG && player1.ammo[GUN_SG])
|
||||
s = GUN_SG;
|
||||
else if (s != GUN_RIFLE && player1->ammo[GUN_RIFLE])
|
||||
else if (s != GUN_RIFLE && player1.ammo[GUN_RIFLE])
|
||||
s = GUN_RIFLE;
|
||||
else
|
||||
s = GUN_FIST;
|
||||
if (s != player1->gunselect)
|
||||
if (s != player1.gunselect)
|
||||
playsoundc(S_WEAPLOAD);
|
||||
player1->gunselect = s;
|
||||
player1.gunselect = s;
|
||||
// conoutf(@"%@ selected", (int)guns[s].name);
|
||||
}
|
||||
|
||||
|
@ -85,9 +86,9 @@ createrays(OFVector3D &from,
|
|||
|
||||
// if lineseg hits entity bounding box
|
||||
bool
|
||||
intersect(dynent *d, const OFVector3D &from, const OFVector3D &to)
|
||||
intersect(DynamicEntity *d, const OFVector3D &from, const OFVector3D &to)
|
||||
{
|
||||
OFVector3D v = to, w = d->o;
|
||||
OFVector3D v = to, w = d.o;
|
||||
const OFVector3D *p;
|
||||
vsub(v, from);
|
||||
vsub(w, from);
|
||||
|
@ -107,9 +108,9 @@ intersect(dynent *d, const OFVector3D &from, const OFVector3D &to)
|
|||
}
|
||||
}
|
||||
|
||||
return (p->x <= d->o.x + d->radius && p->x >= d->o.x - d->radius &&
|
||||
p->y <= d->o.y + d->radius && p->y >= d->o.y - d->radius &&
|
||||
p->z <= d->o.z + d->aboveeye && p->z >= d->o.z - d->eyeheight);
|
||||
return (p->x <= d.o.x + d.radius && p->x >= d.o.x - d.radius &&
|
||||
p->y <= d.o.y + d.radius && p->y >= d.o.y - d.radius &&
|
||||
p->z <= d.o.z + d.aboveeye && p->z >= d.o.z - d.eyeheight);
|
||||
}
|
||||
|
||||
OFString *
|
||||
|
@ -117,13 +118,13 @@ playerincrosshair()
|
|||
{
|
||||
if (demoplayback)
|
||||
return NULL;
|
||||
loopv(players)
|
||||
{
|
||||
dynent *o = players[i];
|
||||
if (!o)
|
||||
|
||||
for (id player in players) {
|
||||
if (player == [OFNull null])
|
||||
continue;
|
||||
if (intersect(o, player1->o, worldpos))
|
||||
return @(o->name);
|
||||
|
||||
if (intersect(player, player1.o, worldpos))
|
||||
return [player name];
|
||||
}
|
||||
|
||||
return nil;
|
||||
|
@ -141,7 +142,7 @@ projreset()
|
|||
|
||||
void
|
||||
newprojectile(OFVector3D &from, OFVector3D &to, float speed, bool local,
|
||||
dynent *owner, int gun)
|
||||
DynamicEntity *owner, int gun)
|
||||
{
|
||||
for (size_t i = 0; i < MAXPROJ; i++) {
|
||||
Projectile *p = projs[i];
|
||||
|
@ -161,29 +162,31 @@ newprojectile(OFVector3D &from, OFVector3D &to, float speed, bool local,
|
|||
}
|
||||
|
||||
void
|
||||
hit(int target, int damage, dynent *d, dynent *at)
|
||||
hit(int target, int damage, DynamicEntity *d, DynamicEntity *at)
|
||||
{
|
||||
if (d == player1)
|
||||
selfdamage(damage, at == player1 ? -1 : -2, at);
|
||||
else if (d->monsterstate)
|
||||
else if (d.monsterstate)
|
||||
monsterpain(d, damage, at);
|
||||
else {
|
||||
addmsg(1, 4, SV_DAMAGE, target, damage, d->lifesequence);
|
||||
playsound(S_PAIN1 + rnd(5), &d->o);
|
||||
addmsg(1, 4, SV_DAMAGE, target, damage, d.lifesequence);
|
||||
OFVector3D loc = d.o;
|
||||
playsound(S_PAIN1 + rnd(5), &loc);
|
||||
}
|
||||
particle_splash(3, damage, 1000, d->o);
|
||||
demodamage(damage, d->o);
|
||||
particle_splash(3, damage, 1000, d.o);
|
||||
demodamage(damage, d.o);
|
||||
}
|
||||
|
||||
const float RL_RADIUS = 5;
|
||||
const float RL_DAMRAD = 7; // hack
|
||||
|
||||
static void
|
||||
radialeffect(dynent *o, const OFVector3D &v, int cn, int qdam, dynent *at)
|
||||
radialeffect(
|
||||
DynamicEntity *o, const OFVector3D &v, int cn, int qdam, DynamicEntity *at)
|
||||
{
|
||||
if (o->state != CS_ALIVE)
|
||||
if (o.state != CS_ALIVE)
|
||||
return;
|
||||
vdist(dist, temp, v, o->o);
|
||||
vdist(dist, temp, v, o.o);
|
||||
dist -= 2; // account for eye distance imprecision
|
||||
if (dist < RL_DAMRAD) {
|
||||
if (dist < 0)
|
||||
|
@ -191,7 +194,7 @@ radialeffect(dynent *o, const OFVector3D &v, int cn, int qdam, dynent *at)
|
|||
int damage = (int)(qdam * (1 - (dist / RL_DAMRAD)));
|
||||
hit(cn, damage, o, at);
|
||||
vmul(temp, (RL_DAMRAD - dist) * damage / 800);
|
||||
vadd(o->vel, temp);
|
||||
vadd(o.vel, temp);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -211,25 +214,32 @@ splash(Projectile *p, const OFVector3D &v, const OFVector3D &vold,
|
|||
if (!p.local)
|
||||
return;
|
||||
radialeffect(player1, v, -1, qdam, p.owner);
|
||||
loopv(players)
|
||||
{
|
||||
if (i == notthisplayer)
|
||||
|
||||
size_t i = 0;
|
||||
for (id player in players) {
|
||||
if (i == notthisplayer) {
|
||||
i++;
|
||||
continue;
|
||||
dynent *o = players[i];
|
||||
if (!o)
|
||||
continue;
|
||||
radialeffect(o, v, i, qdam, p.owner);
|
||||
}
|
||||
dvector &mv = getmonsters();
|
||||
loopv(mv) if (i != notthismonster)
|
||||
radialeffect(mv[i], v, i, qdam, p.owner);
|
||||
|
||||
if (player != [OFNull null])
|
||||
radialeffect(player, v, i, qdam, p.owner);
|
||||
|
||||
i++;
|
||||
}
|
||||
|
||||
i = 0;
|
||||
for (DynamicEntity *monster in getmonsters())
|
||||
if (i != notthismonster)
|
||||
radialeffect(monster, v, i, qdam, p.owner);
|
||||
}
|
||||
}
|
||||
|
||||
inline void
|
||||
projdamage(dynent *o, Projectile *p, OFVector3D &v, int i, int im, int qdam)
|
||||
projdamage(
|
||||
DynamicEntity *o, Projectile *p, OFVector3D &v, int i, int im, int qdam)
|
||||
{
|
||||
if (o->state != CS_ALIVE)
|
||||
if (o.state != CS_ALIVE)
|
||||
return;
|
||||
if (intersect(o, p.o, v)) {
|
||||
splash(p, v, p.o, i, im, qdam);
|
||||
|
@ -246,8 +256,8 @@ moveprojectiles(float time)
|
|||
if (!p.inuse)
|
||||
continue;
|
||||
|
||||
int qdam = guns[p.gun].damage * (p.owner->quadmillis ? 4 : 1);
|
||||
if (p.owner->monsterstate)
|
||||
int qdam = guns[p.gun].damage * (p.owner.quadmillis ? 4 : 1);
|
||||
if (p.owner.monsterstate)
|
||||
qdam /= MONSTERDAMAGEFACTOR;
|
||||
vdist(dist, v, p.o, p.to);
|
||||
float dtime = dist * 1000 / p.speed;
|
||||
|
@ -256,19 +266,17 @@ moveprojectiles(float time)
|
|||
vmul(v, time / dtime);
|
||||
vadd(v, p.o) if (p.local)
|
||||
{
|
||||
loopv(players)
|
||||
{
|
||||
dynent *o = players[i];
|
||||
if (!o)
|
||||
continue;
|
||||
projdamage(o, p, v, i, -1, qdam);
|
||||
}
|
||||
for (id player in players)
|
||||
if (player != [OFNull null])
|
||||
projdamage(player, p, v, i, -1, qdam);
|
||||
|
||||
if (p.owner != player1)
|
||||
projdamage(player1, p, v, -1, -1, qdam);
|
||||
dvector &mv = getmonsters();
|
||||
loopv(mv) if (!vreject(mv[i]->o, v, 10.0f) &&
|
||||
mv[i] != p.owner)
|
||||
projdamage(mv[i], p, v, -1, i, qdam);
|
||||
|
||||
for (DynamicEntity *monster in getmonsters())
|
||||
if (!vreject(monster.o, v, 10.0f) &&
|
||||
monster != p.owner)
|
||||
projdamage(monster, p, v, -1, i, qdam);
|
||||
}
|
||||
if (p.inuse) {
|
||||
if (time == dtime)
|
||||
|
@ -288,11 +296,12 @@ moveprojectiles(float time)
|
|||
}
|
||||
}
|
||||
|
||||
// create visual effect from a shot
|
||||
void
|
||||
shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d,
|
||||
bool local) // create visual effect from a shot
|
||||
shootv(int gun, OFVector3D &from, OFVector3D &to, DynamicEntity *d, bool local)
|
||||
{
|
||||
playsound(guns[gun].sound, d == player1 ? NULL : &d->o);
|
||||
OFVector3D loc = d.o;
|
||||
playsound(guns[gun].sound, d == player1 ? NULL : &loc);
|
||||
int pspeed = 25;
|
||||
switch (gun) {
|
||||
case GUN_FIST:
|
||||
|
@ -313,7 +322,7 @@ shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d,
|
|||
case GUN_ICEBALL:
|
||||
case GUN_SLIMEBALL:
|
||||
pspeed = guns[gun].projspeed;
|
||||
if (d->monsterstate)
|
||||
if (d.monsterstate)
|
||||
pspeed /= 2;
|
||||
newprojectile(from, to, (float)pspeed, local, d, gun);
|
||||
break;
|
||||
|
@ -326,26 +335,27 @@ shootv(int gun, OFVector3D &from, OFVector3D &to, dynent *d,
|
|||
}
|
||||
|
||||
void
|
||||
hitpush(int target, int damage, dynent *d, dynent *at, OFVector3D &from,
|
||||
OFVector3D &to)
|
||||
hitpush(int target, int damage, DynamicEntity *d, DynamicEntity *at,
|
||||
OFVector3D &from, OFVector3D &to)
|
||||
{
|
||||
hit(target, damage, d, at);
|
||||
vdist(dist, v, from, to);
|
||||
vmul(v, damage / dist / 50);
|
||||
vadd(d->vel, v);
|
||||
vadd(d.vel, v);
|
||||
}
|
||||
|
||||
void
|
||||
raydamage(dynent *o, OFVector3D &from, OFVector3D &to, dynent *d, int i)
|
||||
raydamage(
|
||||
DynamicEntity *o, OFVector3D &from, OFVector3D &to, DynamicEntity *d, int i)
|
||||
{
|
||||
if (o->state != CS_ALIVE)
|
||||
if (o.state != CS_ALIVE)
|
||||
return;
|
||||
int qdam = guns[d->gunselect].damage;
|
||||
if (d->quadmillis)
|
||||
int qdam = guns[d.gunselect].damage;
|
||||
if (d.quadmillis)
|
||||
qdam *= 4;
|
||||
if (d->monsterstate)
|
||||
if (d.monsterstate)
|
||||
qdam /= MONSTERDAMAGEFACTOR;
|
||||
if (d->gunselect == GUN_SG) {
|
||||
if (d.gunselect == GUN_SG) {
|
||||
int damage = 0;
|
||||
loop(r, SGRAYS) if (intersect(o, from, sg[r])) damage += qdam;
|
||||
if (damage)
|
||||
|
@ -355,67 +365,67 @@ raydamage(dynent *o, OFVector3D &from, OFVector3D &to, dynent *d, int i)
|
|||
}
|
||||
|
||||
void
|
||||
shoot(dynent *d, OFVector3D &targ)
|
||||
shoot(DynamicEntity *d, const OFVector3D &targ)
|
||||
{
|
||||
int attacktime = lastmillis - d->lastaction;
|
||||
if (attacktime < d->gunwait)
|
||||
int attacktime = lastmillis - d.lastaction;
|
||||
if (attacktime < d.gunwait)
|
||||
return;
|
||||
d->gunwait = 0;
|
||||
if (!d->attacking)
|
||||
d.gunwait = 0;
|
||||
if (!d.attacking)
|
||||
return;
|
||||
d->lastaction = lastmillis;
|
||||
d->lastattackgun = d->gunselect;
|
||||
if (!d->ammo[d->gunselect]) {
|
||||
d.lastaction = lastmillis;
|
||||
d.lastattackgun = d.gunselect;
|
||||
if (!d.ammo[d.gunselect]) {
|
||||
playsoundc(S_NOAMMO);
|
||||
d->gunwait = 250;
|
||||
d->lastattackgun = -1;
|
||||
d.gunwait = 250;
|
||||
d.lastattackgun = -1;
|
||||
return;
|
||||
}
|
||||
if (d->gunselect)
|
||||
d->ammo[d->gunselect]--;
|
||||
OFVector3D from = d->o;
|
||||
if (d.gunselect)
|
||||
d.ammo[d.gunselect]--;
|
||||
OFVector3D from = d.o;
|
||||
OFVector3D to = targ;
|
||||
from.z -= 0.2f; // below eye
|
||||
|
||||
vdist(dist, unitv, from, to);
|
||||
vdiv(unitv, dist);
|
||||
OFVector3D kickback = unitv;
|
||||
vmul(kickback, guns[d->gunselect].kickamount * -0.01f);
|
||||
vadd(d->vel, kickback);
|
||||
if (d->pitch < 80.0f)
|
||||
d->pitch += guns[d->gunselect].kickamount * 0.05f;
|
||||
vmul(kickback, guns[d.gunselect].kickamount * -0.01f);
|
||||
vadd(d.vel, kickback);
|
||||
if (d.pitch < 80.0f)
|
||||
d.pitch += guns[d.gunselect].kickamount * 0.05f;
|
||||
|
||||
if (d->gunselect == GUN_FIST || d->gunselect == GUN_BITE) {
|
||||
if (d.gunselect == GUN_FIST || d.gunselect == GUN_BITE) {
|
||||
vmul(unitv, 3); // punch range
|
||||
to = from;
|
||||
vadd(to, unitv);
|
||||
}
|
||||
if (d->gunselect == GUN_SG)
|
||||
if (d.gunselect == GUN_SG)
|
||||
createrays(from, to);
|
||||
|
||||
if (d->quadmillis && attacktime > 200)
|
||||
if (d.quadmillis && attacktime > 200)
|
||||
playsoundc(S_ITEMPUP);
|
||||
shootv(d->gunselect, from, to, d, true);
|
||||
if (!d->monsterstate)
|
||||
addmsg(1, 8, SV_SHOT, d->gunselect, (int)(from.x * DMF),
|
||||
shootv(d.gunselect, from, to, d, true);
|
||||
if (!d.monsterstate)
|
||||
addmsg(1, 8, SV_SHOT, d.gunselect, (int)(from.x * DMF),
|
||||
(int)(from.y * DMF), (int)(from.z * DMF), (int)(to.x * DMF),
|
||||
(int)(to.y * DMF), (int)(to.z * DMF));
|
||||
d->gunwait = guns[d->gunselect].attackdelay;
|
||||
d.gunwait = guns[d.gunselect].attackdelay;
|
||||
|
||||
if (guns[d->gunselect].projspeed)
|
||||
if (guns[d.gunselect].projspeed)
|
||||
return;
|
||||
|
||||
loopv(players)
|
||||
{
|
||||
dynent *o = players[i];
|
||||
if (!o)
|
||||
continue;
|
||||
raydamage(o, from, to, d, i);
|
||||
size_t i = 0;
|
||||
for (id player in players) {
|
||||
if (player != [OFNull null])
|
||||
raydamage(player, from, to, d, i);
|
||||
i++;
|
||||
}
|
||||
|
||||
dvector &v = getmonsters();
|
||||
loopv(v) if (v[i] != d) raydamage(v[i], from, to, d, -2);
|
||||
for (DynamicEntity *monster in getmonsters())
|
||||
if (monster != d)
|
||||
raydamage(monster, from, to, d, -2);
|
||||
|
||||
if (d->monsterstate)
|
||||
if (d.monsterstate)
|
||||
raydamage(player1, from, to, d, -1);
|
||||
}
|
||||
|
|
11
src/world.mm
11
src/world.mm
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
extern OFString *entnames[]; // lookup from map entities above to strings
|
||||
|
||||
sqr *world = NULL;
|
||||
|
@ -9,9 +11,10 @@ int sfactor, ssize, cubicsize, mipsize;
|
|||
|
||||
header hdr;
|
||||
|
||||
// set all cubes with "tag" to space, if tag is 0 then reset ALL tagged cubes
|
||||
// according to type
|
||||
void
|
||||
settag(int tag, int type) // set all cubes with "tag" to space, if tag is 0 then
|
||||
// reset ALL tagged cubes according to type
|
||||
settag(int tag, int type)
|
||||
{
|
||||
int maxx = 0, maxy = 0, minx = ssize, miny = ssize;
|
||||
loop(x, ssize) loop(y, ssize)
|
||||
|
@ -263,7 +266,7 @@ closestent() // used for delent and edit mode ent display
|
|||
if (e.type == NOTUSED)
|
||||
continue;
|
||||
OFVector3D v = OFMakeVector3D(e.x, e.y, e.z);
|
||||
vdist(dist, t, player1->o, v);
|
||||
vdist(dist, t, player1.o, v);
|
||||
if (dist < bdist) {
|
||||
best = i;
|
||||
bdist = dist;
|
||||
|
@ -345,7 +348,7 @@ newentity(int x, int y, int z, OFString *what, int v1, int v2, int v3, int v4)
|
|||
case TELEDEST:
|
||||
e.attr2 = (uchar)e.attr1;
|
||||
case PLAYERSTART:
|
||||
e.attr1 = (int)player1->yaw;
|
||||
e.attr1 = (int)player1.yaw;
|
||||
break;
|
||||
}
|
||||
addmsg(1, 10, SV_EDITENT, ents.length(), type, e.x, e.y, e.z, e.attr1,
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
extern bool hasoverbright;
|
||||
|
||||
VAR(lightscale, 1, 4, 100);
|
||||
|
@ -201,11 +203,11 @@ cleardlights()
|
|||
|
||||
void
|
||||
dodynlight(const OFVector3D &vold, const OFVector3D &v, int reach, int strength,
|
||||
dynent *owner)
|
||||
DynamicEntity *owner)
|
||||
{
|
||||
if (!reach)
|
||||
reach = dynlight;
|
||||
if (owner->monsterstate)
|
||||
if (owner.monsterstate)
|
||||
reach = reach / 2;
|
||||
if (!reach)
|
||||
return;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
#define NUMRAYS 512
|
||||
|
||||
float rdist[NUMRAYS];
|
||||
|
@ -26,10 +28,10 @@ computeraytable(float vx, float vy)
|
|||
|
||||
odist = getvar(@"fog") * 1.5f;
|
||||
|
||||
float apitch = (float)fabs(player1->pitch);
|
||||
float apitch = (float)fabs(player1.pitch);
|
||||
float af = getvar(@"fov") / 2 + apitch / 1.5f + 3;
|
||||
float byaw = (player1->yaw - 90 + af) / 360 * PI2;
|
||||
float syaw = (player1->yaw - 90 - af) / 360 * PI2;
|
||||
float byaw = (player1.yaw - 90 + af) / 360 * PI2;
|
||||
float syaw = (player1.yaw - 90 - af) / 360 * PI2;
|
||||
|
||||
loopi(NUMRAYS)
|
||||
{
|
||||
|
|
|
@ -4,6 +4,8 @@
|
|||
|
||||
#include "cube.h"
|
||||
|
||||
#import "DynamicEntity.h"
|
||||
|
||||
void
|
||||
render_wall(sqr *o, sqr *s, int x1, int y1, int x2, int y2, int mip, sqr *d1,
|
||||
sqr *d2, bool topleft)
|
||||
|
@ -135,7 +137,7 @@ render_seg_new(
|
|||
// first collect occlusion information for this block
|
||||
for (int oy = y; oy < ys; oy++) {
|
||||
SWS(w, ox, oy, sz)->occluded =
|
||||
isoccluded(player1->o.x, player1->o.y,
|
||||
isoccluded(player1.o.x, player1.o.y,
|
||||
(float)(ox << mip), (float)(oy << mip), fsize);
|
||||
}
|
||||
}
|
||||
|
@ -144,8 +146,8 @@ render_seg_new(
|
|||
int pvy = (int)vy >> mip;
|
||||
if (pvx >= 0 && pvy >= 0 && pvx < sz && pvy < sz) {
|
||||
// SWS(w,vxx,vyy,sz)->occluded = 0;
|
||||
SWS(w, pvx, pvy, sz)->occluded =
|
||||
0; // player cell never occluded
|
||||
// player cell never occluded
|
||||
SWS(w, pvx, pvy, sz)->occluded = 0;
|
||||
}
|
||||
|
||||
#define df(x) s->floor - (x->vdelta / 4.0f)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue