ddnet/src/game/game_server.cpp
2007-05-22 15:03:32 +00:00

2123 lines
47 KiB
C++

#include <stdlib.h>
#include <string.h>
#include "game.h"
using namespace baselib;
// --------- PHYSICS TWEAK! --------
const float ground_control_speed = 7.0f;
const float ground_control_accel = 2.0f;
const float ground_friction = 0.5f;
const float ground_jump_speed = 12.0f;
const float air_control_speed = 3.5f;
const float air_control_accel = 1.2f;
const float air_friction = 0.95f;
const float hook_length = 32*10.0f;
const float hook_fire_speed = 45.0f;
const float hook_drag_accel = 3.0f;
const float hook_drag_speed = 15.0f;
const float gravity = 0.5f;
class player* get_player(int index);
void create_healthmod(vec2 p, int amount);
void create_explosion(vec2 p, int owner = -1, bool bnodamage = false);
void create_smoke(vec2 p);
void create_sound(vec2 pos, int sound, int loopflags = 0);
class player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, class entity* notthis = 0);
// TODO: rewrite this smarter!
void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
{
// do the move
vec2 pos = *inout_pos;
vec2 vel = *inout_vel;
float distance = length(vel);
int max = (int)distance;
vec2 offsets[4] = { vec2(-size.x/2, -size.y/2), vec2( size.x/2, -size.y/2),
vec2(-size.x/2, size.y/2), vec2( size.x/2, size.y/2)};
if(distance > 0.00001f)
{
vec2 old_pos = pos;
for(int i = 0; i <= max; i++)
{
float amount = i/(float)max;
if(max == 0)
amount = 0;
vec2 new_pos = pos + vel*amount; // TODO: this row is not nice
for(int p = 0; p < 4; p++)
{
vec2 np = new_pos+offsets[p];
vec2 op = old_pos+offsets[p];
if(col_check_point(np))
{
int affected = 0;
if(col_check_point(np.x, op.y))
{
vel.x = -vel.x*elasticity;
pos.x = old_pos.x;
new_pos.x = old_pos.x;
affected++;
}
if(col_check_point(op.x, np.y))
{
vel.y = -vel.y*elasticity;
pos.y = old_pos.y;
new_pos.y = old_pos.y;
affected++;
}
if(!affected)
{
new_pos = old_pos;
pos = old_pos;
vel *= -elasticity;
}
}
}
old_pos = new_pos;
}
pos = old_pos;
}
*inout_pos = pos;
*inout_vel = vel;
}
// TODO: rewrite this smarter!
bool intersect_line(vec2 pos0, vec2 pos1, vec2 *out)
{
float d = distance(pos0, pos1);
for(float f = 0; f < d; f++)
{
float a = f/d;
vec2 pos = mix(pos0, pos1, a);
if(col_check_point(pos))
{
if(out)
*out = pos;
return true;
}
}
if(out)
*out = pos1;
return false;
}
//
class event_handler
{
static const int MAX_EVENTS = 128;
static const int MAX_DATASIZE = 128*4;
int types[MAX_EVENTS]; // TODO: remove some of these arrays
int offsets[MAX_EVENTS];
int sizes[MAX_EVENTS];
char data[MAX_DATASIZE];
int current_offset;
int num_events;
public:
event_handler()
{
num_events = 0;
}
void *create(int type, int size)
{
void *p = &data[current_offset];
offsets[num_events] = current_offset;
types[num_events] = type;
sizes[num_events] = size;
current_offset += size;
num_events++;
return p;
}
void clear()
{
num_events = 0;
current_offset = 0;
}
void snap(int snapping_client)
{
for(int i = 0; i < num_events; i++)
{
void *d = snap_new_item(types[i], i, sizes[i]);
mem_copy(d, &data[offsets[i]], sizes[i]);
}
}
};
static event_handler events;
/*
template<typename T, int SIZE>
class pool
{
struct element
{
int next_free;
T data;
};
element elements[SIZE];
int first_free;
public:
pool()
{
first_free = 0;
for(int i = 0; i < SIZE; i++)
elements[i].next_free = i+1;
elements[SIZE-1].next_free = -1;
}
};*/
// a basic entity
class entity
{
private:
friend class game_world;
friend class player;
entity *prev_entity;
entity *next_entity;
int index;
public:
vec2 pos;
float proximity_radius;
unsigned flags;
int objtype;
enum
{
FLAG_DESTROY=0x00000001,
};
entity(int objtype)
{
this->objtype = objtype;
pos = vec2(0,0);
flags = 0;
proximity_radius = 0;
}
virtual ~entity()
{
}
virtual void destroy() { delete this; }
virtual void tick() {}
virtual void snap(int snapping_client) {}
virtual bool take_damage(vec2 force, int dmg, int from) { return true; }
};
class powerup : public entity
{
public:
static const int phys_size = 14;
enum
{
POWERUP_FLAG_HOOKABLE = 1 << 0,
};
vec2 vel;
class player* playerhooked;
int type;
int id;
int subtype; // weapon type for instance?
int numitems; // number off powerup items
int flags;
int spawntick;
powerup(int _type, int _subtype = 0, int _numitems = 0, int _flags = 0);
static void spawnrandom(int _lifespan);
void tick();
void snap(int snapping_client);
};
// game world. handles all entities
class game_world
{
public:
entity *first_entity;
game_world()
{
first_entity = 0x0;
}
int find_entities(vec2 pos, float radius, entity **ents, int max)
{
int num = 0;
for(entity *ent = first_entity; ent; ent = ent->next_entity)
{
if(distance(ent->pos, pos) < radius+ent->proximity_radius)
{
ents[num] = ent;
num++;
if(num == max)
break;
}
}
return num;
}
int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes)
{
int num = 0;
for(entity *ent = first_entity; ent; ent = ent->next_entity)
{
for (int i = 0; i < maxtypes; i++)
{
if (ent->objtype != types[i])
continue;
if(distance(ent->pos, pos) < radius+ent->proximity_radius)
{
ents[num] = ent;
num++;
if(num == max)
break;
}
}
}
return num;
}
void insert_entity(entity *ent)
{
// insert it
if(first_entity)
first_entity->prev_entity = ent;
ent->next_entity = first_entity;
ent->prev_entity = 0x0;
first_entity = ent;
}
void destroy_entity(entity *ent)
{
ent->flags |= entity::FLAG_DESTROY;
// call destroy
//remove_entity(ent);
//ent->destroy();
}
void remove_entity(entity *ent)
{
// remove
if(ent->prev_entity)
ent->prev_entity->next_entity = ent->next_entity;
else
first_entity = ent->next_entity;
if(ent->next_entity)
ent->next_entity->prev_entity = ent->prev_entity;
}
//
void snap(int snapping_client)
{
for(entity *ent = first_entity; ent; ent = ent->next_entity)
ent->snap(snapping_client);
}
void tick()
{
// update all objects
for(entity *ent = first_entity; ent; ent = ent->next_entity)
ent->tick();
// destroy objects marked for destruction
entity *ent = first_entity;
while(ent)
{
entity *next = ent->next_entity;
if(ent->flags&entity::FLAG_DESTROY)
{
remove_entity(ent);
ent->destroy();
}
ent = next;
}
}
};
static game_world world;
// projectile entity
class projectile : public entity
{
public:
enum
{
PROJECTILE_FLAGS_EXPLODE = 1 << 0,
};
vec2 vel;
entity* powner;
int lifespan;
int id;
int owner;
int type;
int flags;
int damage;
int sound_impact;
float force;
projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags = 0, float force = 0.0f, int sound_impact = -1) :
entity(OBJTYPE_PROJECTILE)
{
static int current_id = 0;
this->id = current_id++;
this->type = type;
this->pos = pos;
this->vel = vel;
this->lifespan = span;
this->owner = owner;
this->powner = powner;
this->flags = flags;
this->force = force;
this->damage = damage;
this->sound_impact = sound_impact;
world.insert_entity(this);
}
void tick()
{
vec2 oldpos = pos;
vel.y += 0.25f;
pos += vel;
lifespan--;
// check player intersection as well
entity* targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner);
if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y))
{
if (lifespan >= 0)
create_sound(pos, sound_impact);
if (flags & PROJECTILE_FLAGS_EXPLODE)
{
create_explosion(oldpos, owner);
}
else if (targetplayer)
{
targetplayer->take_damage(normalize(vel) * force, damage, owner);
}
world.destroy_entity(this);
}
}
void snap(int snapping_client)
{
obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile));
proj->x = (int)pos.x;
proj->y = (int)pos.y;
proj->vx = (int)vel.x;
proj->vy = (int)vel.y;
proj->type = type;
}
};
// player entity
class player : public entity
{
public:
static const int phys_size = 28;
enum
{
WEAPON_NEEDRELOAD = 1 << 0,
WEAPON_DROPONUNEQUIP = 1 << 1,
WEAPON_DRAWSAMMO = 1 << 2,
WEAPON_HASSECONDARY = 1 << 3,
WEAPON_ISACTIVE = 1 << 4, // has the item
WEAPON_AUTOFIRE = 1 << 5,
WEAPON_PROJECTILETYPE_GUN = 0,
WEAPON_PROJECTILETYPE_ROCKET = 1,
WEAPON_PROJECTILETYPE_SHOTGUN = 2,
// Gun
// modifiers
MODIFIER_HASACTIVATIONS = 1 << 0,
MODIFIER_TIMELIMITED = 1 << 1,
MODIFIER_ISACTIVE = 1 << 2,
MODIFIER_NEEDSACTIVATION = 1 << 3,
MODIFIER_RETURNFLAGS_OVERRIDEWEAPON = 1 << 0,
MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY = 1 << 1,
MODIFIER_RETURNFLAGS_OVERRIDEPOSITION = 1 << 2,
MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY = 1 << 3,
};
class weapon
{
public:
entity* hitobjects[10];
int numobjectshit; // for melee, so we don't hit the same object more than once per bash
int weapontype;
int equiptime;
int unequiptime;
int numammo;
int magsize;
int nummagazines;
int flags;
int firetime;
int reloadtime;
int projectileclass;
int damage;
int sound_fire;
int sound_equip;
int sound_impact;
int sound_impact_projectile;
int visualtimeattack;
float projectilevel;
float projectilespan;
float reach; // for melee
float force;
float recoilforce;
float projoffsety;
float projoffsetx;
weapon()
{
weapontype = 0;
numammo = 0;
flags = 0;
reloadtime = 0;
projectileclass = 0;
numobjectshit = 0;
reach = 0.0f;
force = 5.0f;
damage = 1;
sound_fire = -1;
sound_equip = -1;
sound_impact = -1;
sound_impact_projectile = -1,
visualtimeattack = 3;
recoilforce = 0.0f;
projoffsety = 0.0f;
projoffsetx = 0.0f;
}
void setgun(int ammo = 10)
{
weapontype = WEAPON_TYPE_GUN;
flags = 0;//WEAPON_DRAWSAMMO;
numammo = ammo;
projectileclass = WEAPON_PROJECTILETYPE_GUN;
firetime = SERVER_TICK_SPEED/10;
magsize = 0;
projectilevel = 30.0f;
projectilespan = 50.0f * 1.0f;
sound_fire = SOUND_FIRE_GUN;
sound_equip = SOUND_EQUIP_GUN;
sound_impact_projectile = SOUND_IMPACT_PROJECTILE_GUN;
projoffsety = -10.0f;
projoffsetx = 24.0f;
}
void setrocket(int ammo = 10)
{
weapontype = WEAPON_TYPE_ROCKET;
flags = WEAPON_DRAWSAMMO;
numammo = ammo;
projectileclass = WEAPON_PROJECTILETYPE_ROCKET;
projectilevel = 15.0f;
projectilespan = 50.0f * 5.0f;
firetime = SERVER_TICK_SPEED * 4/5;
magsize = 0;
recoilforce = 5.0f;
sound_fire = SOUND_FIRE_ROCKET;
sound_equip = SOUND_EQUIP_ROCKET;
sound_impact_projectile = SOUND_IMPACT_PROJECTILE_ROCKET;
projoffsety = -17.0f;
projoffsetx = 24.0f;
}
/*void setsniper(int ammo = 10)
{
weapontype = WEAPON_TYPE_SNIPER;
flags = WEAPON_DRAWSAMMO | WEAPON_HASSECONDARY | WEAPON_NEEDRELOAD;
numammo = ammo;
projectileclass = WEAPON_PROJECTILETYPE_SNIPER;
projectilevel = 30.0f;
projectilespan = 50.0f * 5.0f;
firetime = SERVER_TICK_SPEED;
reloadtime = SERVER_TICK_SPEED/2;
magsize = 2;
recoilforce = 20.0f;
}*/
void setshotgun(int ammo = 10)
{
weapontype = WEAPON_TYPE_SHOTGUN;
flags = WEAPON_DRAWSAMMO | WEAPON_NEEDRELOAD;
numammo = ammo;
projectileclass = WEAPON_PROJECTILETYPE_SHOTGUN;
projectilevel = 30.0f;
projectilespan = 50.0f * 5.0f;
firetime = SERVER_TICK_SPEED/2;
reloadtime = SERVER_TICK_SPEED/2;
magsize = 2;
damage = 3;
recoilforce = 5.0f;
sound_fire = SOUND_FIRE_SHOTGUN;
sound_equip = SOUND_EQUIP_SHOTGUN;
sound_impact_projectile = SOUND_IMPACT_PROJECTILE_SHOTGUN;
projoffsety = -17.0f;
projoffsetx = 24.0f;
}
void setmelee(int ammo = 10)
{
weapontype = WEAPON_TYPE_MELEE;
flags = 0;//WEAPON_AUTOFIRE;
numammo = ammo;
projectileclass = -1;
firetime = SERVER_TICK_SPEED/5;
reloadtime = 0;
magsize = 2;
numobjectshit = 0;
reach = 15.0f;
damage = 1;
sound_fire = SOUND_FIRE_MELEE;
sound_equip = SOUND_EQUIP_MELEE;
sound_impact = SOUND_PLAYER_IMPACT;
}
void settype()
{
switch(weapontype)
{
case WEAPON_TYPE_GUN:
{
setgun();
break;
}
case WEAPON_TYPE_ROCKET:
{
setrocket();
break;
}
/*case WEAPON_TYPE_SNIPER:
{
setsniper();
break;
}*/
case WEAPON_TYPE_SHOTGUN:
{
setshotgun();
break;
}
case WEAPON_TYPE_MELEE:
{
setmelee();
break;
}
default:
break;
}
}
int activate(player* player)
{
// create sound event for fire
int projectileflags = 0;
create_sound(player->pos, sound_fire);
switch (weapontype)
{
case WEAPON_TYPE_ROCKET:
projectileflags |= projectile::PROJECTILE_FLAGS_EXPLODE;
case WEAPON_TYPE_GUN:
//case WEAPON_TYPE_SNIPER:
case WEAPON_TYPE_SHOTGUN:
{
if (flags & WEAPON_DRAWSAMMO)
numammo--;
// Create projectile
new projectile(projectileclass, player->client_id, player->pos+vec2(0,projoffsety)+player->direction*projoffsetx, player->direction*projectilevel, projectilespan, player, damage, projectileflags, force, sound_impact_projectile);
// recoil force if any
if (recoilforce > 0.0f)
{
vec2 dir(player->direction.x,0.5);
if (dir.x == 0.0f)
dir.x = 0.5f;
else
dir = normalize(dir);
player->vel -= dir * recoilforce;
}
return firetime;
}
case WEAPON_TYPE_MELEE:
{
// Start bash sequence
numobjectshit = 0;
return firetime;
}
default:
return 0;
}
}
void update(player* owner, int fire_timeout)
{
switch(weapontype)
{
case WEAPON_TYPE_MELEE:
{
// No more melee
if (fire_timeout <= 0)
return;
// only one that needs update (for now)
// do selection for the weapon and bash anything in it
// check if we hit anything along the way
int type = OBJTYPE_PLAYER;
entity *ents[64];
vec2 dir = owner->pos + owner->direction * reach;
float radius = length(dir * 0.5f);
vec2 center = owner->pos + dir * 0.5f;
int num = world.find_entities(center, radius, ents, 64, &type, 1);
for (int i = 0; i < num; i++)
{
// Check if entity is a player
if (ents[i] == owner)
continue;
// make sure we haven't hit this object before
bool balreadyhit = false;
for (int j = 0; j < numobjectshit; j++)
{
if (hitobjects[j] == ents[i])
balreadyhit = true;
}
if (balreadyhit)
continue;
// check so we are sufficiently close
if (distance(ents[i]->pos, owner->pos) > (owner->phys_size * 2.0f))
continue;
// hit a player, give him damage and stuffs...
// create sound for bash
create_sound(ents[i]->pos, sound_impact);
// set his velocity to fast upward (for now)
create_smoke(ents[i]->pos);
hitobjects[numobjectshit++] = ents[i];
ents[i]->take_damage(vec2(0,10.0f), damage, owner->client_id);
player* target = (player*)ents[i];
vec2 dir;
if (length(target->pos - owner->pos) > 0.0f)
dir = normalize(target->pos - owner->pos);
else
dir = vec2(0,-1);
target->vel += dir * 10.0f + vec2(0,-10.0f);
}
break;
}
default:
break;
}
}
};
class modifier
{
public:
vec2 activationdir;
entity* hitobjects[10];
int numobjectshit;
float velocity;
int modifiertype;
int duration;
int numactivations;
int activationtime;
int cooldown;
int movetime;
int visualtimeattack;
int currentactivation;
int currentmovetime;
int currentcooldown;
int damage;
int flags;
int sound_impact;
int sound_activate;
modifier()
{
modifiertype = 0;
duration = 0;
numobjectshit = 0;
numactivations = 0;
activationtime = 0;
cooldown = 0;
movetime = 0;
currentactivation = 0;
currentmovetime = 0;
currentcooldown =0;
damage = 0;
flags = 0;
activationdir = vec2(0.0f, 1.0f);
velocity = 0.0f;
visualtimeattack = 0;
sound_impact = -1;
}
void setninja()
{
modifiertype = MODIFIER_TYPE_NINJA;
duration = SERVER_TICK_SPEED * 15;
numactivations = -1;
movetime = SERVER_TICK_SPEED / 5;
activationtime = SERVER_TICK_SPEED / 2;
cooldown = SERVER_TICK_SPEED;
currentactivation = 0;
currentmovetime = 0;
numobjectshit = 0;
damage = 3;
flags = MODIFIER_TIMELIMITED | MODIFIER_NEEDSACTIVATION;
velocity = 50.0f;
visualtimeattack = 3;
sound_impact = SOUND_PLAYER_IMPACT_NINJA;
sound_activate = SOUND_FIRE_NINJA;
}
void settimefield()
{
modifiertype = MODIFIER_TYPE_TIMEFIELD;
duration = SERVER_TICK_SPEED * 10;
numactivations = -1;
activationtime = SERVER_TICK_SPEED;
numobjectshit = 0;
currentactivation = 0;
flags = MODIFIER_TIMELIMITED;
velocity = 0.0f;
}
void settype()
{
switch (modifiertype)
{
case MODIFIER_TYPE_NINJA:
{
setninja();
break;
}
case MODIFIER_TYPE_TIMEFIELD:
{
settimefield();
break;
}
default:
break;
}
}
int updateninja(player* player)
{
if (currentactivation <= 0)
return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
currentactivation--;
currentmovetime--;
if (currentmovetime == 0)
{
// reset player velocity
player->vel *= 0.2f;
//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
}
if (currentmovetime > 0)
{
// Set player velocity
player->vel = activationdir * velocity;
vec2 oldpos = player->pos;
move_box(&player->pos, &player->vel, vec2(player->phys_size, player->phys_size), 0.0f);
// reset velocity so the client doesn't predict stuff
player->vel = vec2(0.0f,0.0f);
if ((currentmovetime % 2) == 0)
{
create_smoke(player->pos);
}
// check if we hit anything along the way
{
int type = OBJTYPE_PLAYER;
entity *ents[64];
vec2 dir = player->pos - oldpos;
float radius = length(dir * 0.5f);
vec2 center = oldpos + dir * 0.5f;
int num = world.find_entities(center, radius, ents, 64, &type, 1);
for (int i = 0; i < num; i++)
{
// Check if entity is a player
if (ents[i] == player)
continue;
// make sure we haven't hit this object before
bool balreadyhit = false;
for (int j = 0; j < numobjectshit; j++)
{
if (hitobjects[j] == ents[i])
balreadyhit = true;
}
if (balreadyhit)
continue;
// check so we are sufficiently close
if (distance(ents[i]->pos, player->pos) > (player->phys_size * 2.0f))
continue;
// hit a player, give him damage and stuffs...
create_sound(ents[i]->pos, sound_impact);
// set his velocity to fast upward (for now)
hitobjects[numobjectshit++] = ents[i];
ents[i]->take_damage(vec2(0,10.0f), damage, player->client_id);
}
}
return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION|MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY;
}
// move char, and check intersection from us to target
return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY;
}
int activateninja(player* player)
{
// ok then, activate ninja
activationdir = player->direction;
currentactivation = activationtime;
currentmovetime = movetime;
currentcooldown = cooldown;
// reset hit objects
numobjectshit = 0;
create_sound(player->pos, SOUND_FIRE_NINJA);
return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
}
int activate(player* player)
{
if (flags & MODIFIER_NEEDSACTIVATION)
{
if (!numactivations)
return 0;
numactivations--;
}
switch (modifiertype)
{
case MODIFIER_TYPE_NINJA:
{
return activateninja(player);
}
/*case MODIFIER_TYPE_TIMEFIELD:
{
updatetimefield();
break;
}*/
default:
return 0;
}
}
int update(player* player)
{
switch (modifiertype)
{
case MODIFIER_TYPE_NINJA:
{
return updateninja(player);
}
/*case MODIFIER_TYPE_TIMEFIELD:
{
updatetimefield();
break;
}*/
default:
return 0;
}
}
};
enum
{
PLAYER_FLAGS_ISRELOADING = 1 << 0,
PLAYER_FLAGS_ISEQUIPPING = 1 << 1,
};
weapon lweapons[WEAPON_NUMWEAPONS];
modifier modifiers[MODIFIER_NUMMODIFIERS];
int iactiveweapon;
int inextweapon;
int equip_time;
int client_id;
int flags;
char name[32];
player_input previnput;
player_input input;
int tick_count;
int damage_taken_tick;
vec2 vel;
vec2 direction;
int jumped;
int airjumped;
//int firing;
int hooking;
int fire_timeout;
int reload_timeout;
int health;
int armor;
int score;
// sounds
int sound_player_jump;
int sound_player_land;
int sound_player_hurt_short;
int sound_player_hurt_long;
int sound_player_spawn;
int sound_player_chain_loop;
int sound_player_chain_impact;
int sound_player_impact;
int sound_player_impact_ninja;
int sound_player_die;
int sound_player_switchweapon;
player* phookedplayer;
powerup* phookedpowerup;
int numhooked;
vec2 hook_pos;
vec2 hook_dir;
player() :
entity(OBJTYPE_PLAYER)
{
reset();
//firing = 0;
// setup weaponflags and stuff
lweapons[WEAPON_TYPE_GUN].setgun();
lweapons[WEAPON_TYPE_ROCKET].setrocket();
//lweapons[WEAPON_TYPE_SNIPER].setsniper();
lweapons[WEAPON_TYPE_SHOTGUN].setshotgun();
lweapons[WEAPON_TYPE_MELEE].setmelee();
modifiers[MODIFIER_TYPE_NINJA].setninja();
modifiers[MODIFIER_TYPE_TIMEFIELD].settimefield();
//modifiers[MODIFIER_TYPE_NINJA].flags |= MODIFIER_ISACTIVE;
sound_player_jump = SOUND_PLAYER_JUMP;
sound_player_hurt_short = SOUND_PLAYER_HURT_SHORT;
sound_player_hurt_long = SOUND_PLAYER_HURT_LONG;
sound_player_spawn = SOUND_PLAYER_SPAWN;
sound_player_chain_loop = SOUND_PLAYER_CHAIN_LOOP;
sound_player_chain_impact = SOUND_PLAYER_CHAIN_IMPACT;
sound_player_impact = SOUND_PLAYER_IMPACT;
sound_player_impact_ninja = SOUND_PLAYER_IMPACT_NINJA;
sound_player_die = SOUND_PLAYER_DIE;
sound_player_switchweapon = SOUND_PLAYER_SWITCHWEAPON;
sound_player_land = SOUND_PLAYER_LAND;
}
void reset()
{
equip_time = 0;
phookedplayer = 0;
numhooked = 0;
proximity_radius = phys_size;
name[0] = 'n';
name[1] = 'o';
name[2] = 'o';
name[3] = 'b';
name[4] = 0;
pos = vec2(100.0f, 0.0f);
vel = vec2(0.0f, 0.0f);
direction = vec2(0.0f, 1.0f);
client_id = -1;
tick_count = 0;
score = 0;
flags = 0;
}
virtual void destroy() { flags = 0; }
void respawn()
{
health = PLAYER_MAXHEALTH;
armor = 0;
hooking = 0;
phookedplayer = 0;
phookedpowerup = 0;
numhooked = 0;
fire_timeout = 0;
reload_timeout = 0;
iactiveweapon = 0;
inextweapon = -1;
equip_time = 0;
jumped = 0;
airjumped = 0;
mem_zero(&input, sizeof(input));
vel = vec2(0.0f, 0.0f);
int start, num;
map_get_type(1, &start, &num);
if(num)
{
mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL);
pos = vec2(sp->x, sp->y);
}
else
pos = vec2(100.0f, -60.0f);
// reset active flags
for (int i = 0; i < WEAPON_NUMWEAPONS; i++)
{
// reset and remove
lweapons[i].settype();
lweapons[i].flags &= ~WEAPON_ISACTIVE;
}
// TEMP REMOVE
/*for (int i = 0; i < WEAPON_NUMWEAPONS; i++)
{
lweapons[i].settype();
lweapons[i].flags |= WEAPON_ISACTIVE;
}*/
lweapons[WEAPON_TYPE_MELEE].flags |= WEAPON_ISACTIVE;
// Add gun as default weapon
iactiveweapon = WEAPON_TYPE_GUN;
lweapons[WEAPON_TYPE_GUN].numammo = 10;
lweapons[WEAPON_TYPE_GUN].flags |= WEAPON_ISACTIVE;
create_sound(pos, sound_player_spawn);
}
bool is_grounded()
{
if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
return true;
if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
return true;
return false;
}
// Disable weapon activation if this returns true
int handlemodifiers()
{
int returnflags = 0;
for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++)
{
if (modifiers[i].flags & MODIFIER_ISACTIVE)
{
modifiers[i].duration--;
modifiers[i].currentcooldown--;
// Check if it should activate
if (modifiers[i].currentcooldown <= 0 &&
(modifiers[i].flags & MODIFIER_NEEDSACTIVATION) &&
input.fire && !(previnput.fire))
{
returnflags |= modifiers[i].activate(this);
}
returnflags |= modifiers[i].update(this);
// remove active if timed out
if (modifiers[i].duration <= 0 && modifiers[i].currentactivation <= 0)
modifiers[i].flags &= ~MODIFIER_ISACTIVE;
}
}
return returnflags;
}
void handleweapon()
{
// handle weapon
if(input.fire && (!previnput.fire || lweapons[iactiveweapon].flags & WEAPON_AUTOFIRE) &&
!(flags & PLAYER_FLAGS_ISEQUIPPING) && !reload_timeout)
{
if(fire_timeout == 0)
{
if (lweapons[iactiveweapon].numammo || !(lweapons[iactiveweapon].flags & WEAPON_DRAWSAMMO))
{
// Decrease ammo
fire_timeout = lweapons[iactiveweapon].activate(this);
}
else if ((lweapons[iactiveweapon].flags & WEAPON_NEEDRELOAD) && lweapons[iactiveweapon].nummagazines)
{
// reload
reload_timeout = lweapons[iactiveweapon].reloadtime;
lweapons[iactiveweapon].nummagazines--;
lweapons[iactiveweapon].numammo = lweapons[iactiveweapon].magsize;
}
}
}
// update active weapon
lweapons[iactiveweapon].update(this, fire_timeout);
}
void handlehook()
{
// handle hook
if(input.hook)
{
if(hooking == 0)
{
hooking = 1;
hook_pos = pos;
hook_dir = direction;
// Sound
create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STARTLOOP);
}
else if(hooking == 1)
{
vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
// Check against other players and powerups first
player* targetplayer = 0;
powerup* targetpowerup = 0;
{
static const int typelist[2] = { OBJTYPE_PLAYER, OBJTYPE_POWERUP};
entity *ents[64];
vec2 dir = new_pos - hook_pos;
float radius = length(dir * 0.5f);
vec2 center = hook_pos + dir * 0.5f;
int num = world.find_entities(center, radius, ents, 64,typelist,2);
for (int i = 0; i < num; i++)
{
// Check if entity is a player
if (ents[i] == this || (targetplayer && targetpowerup))
continue;
if (!targetplayer && ents[i]->objtype == OBJTYPE_PLAYER)
{
// temp, set hook pos to our position
if (((player*)ents[i])->phookedplayer != this)
targetplayer = (player*)ents[i];
}
else if (!targetpowerup && ents[i]->objtype == OBJTYPE_POWERUP &&
(((powerup*)ents[i])->flags & powerup::POWERUP_FLAG_HOOKABLE))
{
targetpowerup = (powerup*)ents[i];
}
}
}
//player* targetplayer = intersect_player(hook_pos, hook_pos, new_pos, this);
if (targetplayer)
{
// So he can't move "normally"
new_pos = targetplayer->pos;
phookedplayer = targetplayer;
targetplayer->numhooked++;
hooking = 3;
create_sound(pos, sound_player_chain_impact);
// stop looping chain sound
create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
}
else if (targetpowerup)
{
new_pos = targetpowerup->pos;
phookedpowerup = targetpowerup;
phookedpowerup->playerhooked = this;
hooking = 4;
create_sound(pos, sound_player_chain_impact);
// stop looping chain sound
create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
}
else if(intersect_line(hook_pos, new_pos, &new_pos))
{
hooking = 2;
create_sound(pos, sound_player_chain_impact);
// stop looping chain sound
create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
}
else if(distance(pos, new_pos) > hook_length)
{
hooking = -1;
create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
}
hook_pos = new_pos;
}
else if(hooking == 2)
{
vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
// the hook as more power to drag you up then down.
// this makes it easier to get on top of an platform
if(hookvel.y > 0)
hookvel.y *= 0.3f;
// the hook will boost it's power if the player wants to move
// in that direction. otherwise it will dampen everything abit
if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right))
hookvel.x *= 0.95f;
else
hookvel.x *= 0.75f;
vec2 new_vel = vel+hookvel;
// check if we are under the legal limit for the hook
if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
vel = new_vel; // no problem. apply
}
else if (hooking == 3)
{
// hmm, force the targetplayer towards us if possible, otherwise us towards them if they are already hooked
if (phookedplayer)
{
if (phookedplayer->hooking > 1)
{
// Drag us towards target player
vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right))
hookvel.x *= 0.95f;
else
hookvel.x *= 0.75f;
// Apply the velocity
// the hook will boost it's power if the player wants to move
// in that direction. otherwise it will dampen everything abit
vec2 new_vel = vel+hookvel;
// check if we are under the legal limit for the hook
if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
vel = new_vel; // no problem. apply
}
else
{
// Drag targetplayer towards us
vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel;
// Apply the velocity
// the hook will boost it's power if the player wants to move
// in that direction. otherwise it will dampen everything abit
vec2 new_vel = phookedplayer->vel+hookvel;
// check if we are under the legal limit for the hook
if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
phookedplayer->vel = new_vel; // no problem. apply
}
hook_pos = phookedplayer->pos;
// if hooked player dies, release the hook
}
else
{
hooking = -1;
phookedplayer = 0;
}
}
else if (hooking == 4)
{
// Have a powerup, drag it towards us
vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel;
// Apply the velocity
// the hook will boost it's power if the player wants to move
// in that direction. otherwise it will dampen everything abit
vec2 new_vel = phookedpowerup->vel+hookvel;
// check if we are under the legal limit for the hook
if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
phookedpowerup->vel = new_vel; // no problem. apply
hook_pos = phookedpowerup->pos;
}
}
else
{
hooking = 0;
hook_pos = pos;
if (phookedplayer)
{
phookedplayer->numhooked--;
phookedplayer = 0;
}
}
}
void getattackticks(int& curattack, int& attacklen, int& visualtimeattack)
{
// time left from current attack (if any)
// first check modifiers (ninja...)
for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++)
{
if ((modifiers[i].flags & (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION)) == (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION))
{
curattack = modifiers[i].currentactivation;
attacklen = modifiers[i].activationtime;
visualtimeattack = modifiers[i].visualtimeattack;
return;
}
}
// otherwise current fire timeout
curattack = fire_timeout;
attacklen = lweapons[iactiveweapon].firetime;
visualtimeattack = lweapons[iactiveweapon].visualtimeattack;
}
virtual void tick()
{
tick_count++;
// fetch some info
bool grounded = is_grounded();
direction = get_direction(input.angle);
// decrease reload timer
if(fire_timeout)
fire_timeout--;
if (reload_timeout)
reload_timeout--;
// Switch weapons
if (flags & PLAYER_FLAGS_ISEQUIPPING)
{
equip_time--;
if (equip_time <= 0)
{
if (inextweapon >= 0)
{
equip_time = SERVER_TICK_SPEED * lweapons[inextweapon].equiptime;
iactiveweapon = inextweapon;
inextweapon = -1;
// Send switch weapon event to client?
}
else
{
flags &= ~PLAYER_FLAGS_ISEQUIPPING;
}
}
}
else if (input.activeweapon && iactiveweapon != (input.activeweapon & ~0x80000000))
{
input.activeweapon &= ~0x80000000;
// check which weapon to activate
if (input.activeweapon >= 0 && input.activeweapon < WEAPON_NUMWEAPONS &&
(lweapons[input.activeweapon].flags & WEAPON_ISACTIVE))
{
inextweapon = input.activeweapon;
equip_time = SERVER_TICK_SPEED * lweapons[iactiveweapon].unequiptime;
// unequip current
flags |= PLAYER_FLAGS_ISEQUIPPING;
create_sound(pos, sound_player_switchweapon);
}
}
// don't do any weapon activations if modifier is currently overriding
int modifierflags = handlemodifiers();
if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEWEAPON))
handleweapon();
handlehook();
// handle movement
if(grounded)
{
if (airjumped)
create_sound(pos, SOUND_PLAYER_LAND);
airjumped = 0;
}
float elast = 0.0f;
if (!numhooked)
{
// I'm hooked by someone, so don't do any movement plz (temp)
if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY))
{
if(grounded)
{
// ground movement
if(input.left)
{
if(vel.x > -ground_control_speed)
{
vel.x -= ground_control_accel;
if(vel.x < -ground_control_speed)
vel.x = -ground_control_speed;
}
}
else if(input.right)
{
if(vel.x < ground_control_speed)
{
vel.x += ground_control_accel;
if(vel.x > ground_control_speed)
vel.x = ground_control_speed;
}
}
else
vel.x *= ground_friction; // ground fiction
}
else
{
// air movement
if(input.left)
{
if(vel.x > -air_control_speed)
vel.x -= air_control_accel;
}
else if(input.right)
{
if(vel.x < air_control_speed)
vel.x += air_control_accel;
}
else
vel.x *= air_friction; // air fiction
}
if(input.jump)
{
if(jumped == 0)
{
if(grounded)
{
create_sound(pos, sound_player_jump);
vel.y = -ground_jump_speed;
jumped++;
}
/*
else if(airjumped == 0)
{
vel.y = -12;
airjumped++;
jumped++;
}*/
}
else if (!grounded)
{
airjumped++;
}
}
else
jumped = 0;
}
// meh, go through all players and stop their hook on me
/*
for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
{
if (ent && ent->objtype == OBJTYPE_PLAYER)
{
player *p = (player*)ent;
if(p != this)
{
float d = distance(pos, p->pos);
vec2 dir = normalize(pos - p->pos);
if(d < phys_size*1.5f)
{
float a = phys_size*1.5f - d;
vel = vel + dir*a;
}
if(p->phookedplayer == this)
{
if(d > phys_size*2.5f)
{
elast = 0.0f;
vel = vel - dir*2.5f;
}
}
}
}
}*/
// gravity
if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
vel.y += gravity;
}
if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEPOSITION))
move_box(&pos, &vel, vec2(phys_size, phys_size), elast);
}
void die()
{
create_sound(pos, sound_player_die);
// release our hooked player
if (phookedplayer)
{
phookedplayer->numhooked--;
phookedplayer = 0;
hooking = -1;
numhooked = 0;
}
respawn();
// meh, go through all players and stop their hook on me
for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
{
if (ent && ent->objtype == OBJTYPE_PLAYER)
{
player* p = (player*)ent;
if (p->phookedplayer == this)
{
p->phookedplayer = 0;
p->hooking = -1;
//p->numhooked--;
}
}
}
}
virtual bool take_damage(vec2 force, int dmg, int from)
{
vel += force;
if(armor)
{
armor -= 1;
dmg--;
}
if(dmg > armor)
{
dmg -= armor;
armor = 0;
health -= dmg;
}
else
armor -= dmg;
/*
int armordmg = (dmg+1)/2;
int healthdmg = dmg-armordmg;
if(armor < armordmg)
{
healthdmg += armordmg - armor;
armor = 0;
}
else
armor -= armordmg;
health -= healthdmg;
*/
// create healthmod indicator
create_healthmod(pos, dmg);
damage_taken_tick = tick_count+50;
// check for death
if(health <= 0)
{
// apply score
if(from != -1)
{
if(from == client_id)
score--;
else
{
player *p = get_player(from);
p->score++;
}
}
die();
return false;
}
if (dmg > 2)
create_sound(pos, sound_player_hurt_long);
else
create_sound(pos, sound_player_hurt_short);
// spawn blood?
return true;
}
virtual void snap(int snaping_client)
{
obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
client_info info;
if(server_getclientinfo(client_id, &info))
snap_encode_string(info.name, player->name, strlen(info.name), 32);
player->x = (int)pos.x;
player->y = (int)pos.y;
player->vx = (int)vel.x;
player->vy = (int)vel.y;
player->emote = EMOTE_NORMAL;
player->ammocount = lweapons[iactiveweapon].numammo;
player->health = 0;
player->armor = 0;
player->local = 0;
player->clientid = client_id;
player->weapon = iactiveweapon;
player->modifier = 0;
for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++)
{
// add active modifiers
if (modifiers[i].flags & MODIFIER_ISACTIVE)
player->modifier |= 1 << i;
}
// get current attack ticks
getattackticks(player->attackticks, player->attacklen, player->visualtimeattack);
if(client_id == snaping_client)
{
player->local = 1;
player->health = health;
player->armor = armor;
}
if(length(vel) > 15.0f)
player->emote = EMOTE_HAPPY;
if(damage_taken_tick > tick_count)
player->emote = EMOTE_PAIN;
if(player->emote == EMOTE_NORMAL)
{
if((tick_count%(50*5)) < 10)
player->emote = EMOTE_BLINK;
}
player->hook_active = hooking>0?1:0;
player->hook_x = (int)hook_pos.x;
player->hook_y = (int)hook_pos.y;
player->angle = input.angle;
player->score = score;
}
};
// POWERUP ///////////////////////
powerup::powerup(int _type, int _subtype, int _numitems, int _flags) :
entity(OBJTYPE_POWERUP)
{
static int current_id = 0;
playerhooked = 0;
id = current_id++;
vel = vec2(0.0f,0.0f);
type = _type;
subtype = _subtype;
numitems = _numitems;
flags = _flags;
// set radius (so it can collide and be hooked and stuff)
proximity_radius = phys_size;
spawntick = -1;
world.insert_entity(this);
}
void powerup::spawnrandom(int _lifespan)
{
return;
/*
vec2 pos;
int start, num;
map_get_type(1, &start, &num);
if(!num)
return;
mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL);
pos = vec2(sp->x, sp->y);
// Check if there already is a powerup at that location
{
int type = OBJTYPE_POWERUP;
entity *ents[64];
int num = world.find_entities(pos, 5.0f, ents, 64,&type,1);
for (int i = 0; i < num; i++)
{
if (ents[i]->objtype == OBJTYPE_POWERUP)
{
// location busy
return;
}
}
}
powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan);
ppower->pos = pos;
if (ppower->type == POWERUP_TYPE_WEAPON)
{
ppower->subtype = rand() % WEAPON_NUMWEAPONS;
ppower->numitems = 10;
}
else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR)
{
ppower->numitems = rand() % 5;
}
ppower->flags |= POWERUP_FLAG_HOOKABLE;*/
}
void powerup::tick()
{
// wait for respawn
if(spawntick > 0)
{
if(server_tick() > spawntick)
spawntick = -1;
else
return;
}
vec2 oldpos = pos;
//vel.y += 0.25f;
pos += vel;
move_box(&pos, &vel, vec2(phys_size, phys_size), 0.0f);
// Check if a player intersected us
vec2 meh;
player* pplayer = intersect_player(pos, pos + vec2(0,16), meh, 0);
if (pplayer)
{
// player picked us up, is someone was hooking us, let them go
if (playerhooked)
playerhooked->hooking = -1;
int respawntime = -1;
switch (type)
{
case POWERUP_TYPE_HEALTH:
{
if(pplayer->health < PLAYER_MAXHEALTH)
{
pplayer->health = min(PLAYER_MAXHEALTH, pplayer->health + numitems);
respawntime = 20;
}
break;
}
case POWERUP_TYPE_ARMOR:
{
if(pplayer->armor < PLAYER_MAXARMOR)
{
pplayer->armor = min(PLAYER_MAXARMOR, pplayer->armor + numitems);
respawntime = 20;
}
break;
}
case POWERUP_TYPE_WEAPON:
{
if (pplayer->lweapons[subtype].flags & player::WEAPON_ISACTIVE)
{
// add ammo
/*
if (pplayer->lweapons[subtype].flags & player::WEAPON_NEEDRELOAD)
{
int numtoadd = min(numitems, pplayer->lweapons[subtype].magsize);
pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numtoadd);
pplayer->lweapons[subtype].nummagazines += (numitems - numtoadd) % pplayer->lweapons[subtype].magsize;
}
else*/
if(pplayer->lweapons[subtype].numammo < 10)
{
respawntime = 20;
pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numitems);
}
}
else
{
pplayer->lweapons[subtype].settype();
pplayer->lweapons[subtype].flags |= player::WEAPON_ISACTIVE;
respawntime = 20;
}
break;
}
case POWERUP_TYPE_NINJA:
{
respawntime = 60;
// reset and activate
pplayer->modifiers[MODIFIER_TYPE_NINJA].settype();
pplayer->modifiers[MODIFIER_TYPE_NINJA].flags |= player::MODIFIER_ISACTIVE;
break;
}
//POWERUP_TYPE_TIMEFIELD = 4,
default:
break;
};
if(respawntime >= 0)
spawntick = server_tick() + server_tickspeed() * respawntime;
//world.destroy_entity(this);
}
}
void powerup::snap(int snapping_client)
{
if(spawntick != -1)
return;
obj_powerup *powerup = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup));
powerup->x = (int)pos.x;
powerup->y = (int)pos.y;
powerup->vx = (int)vel.x;
powerup->vy = (int)vel.y;
powerup->type = type;
powerup->subtype = subtype;
}
// POWERUP END ///////////////////////
static player players[MAX_CLIENTS];
player *get_player(int index)
{
return &players[index];
}
void create_healthmod(vec2 p, int amount)
{
ev_healthmod *ev = (ev_healthmod *)events.create(EVENT_HEALTHMOD, sizeof(ev_healthmod));
ev->x = (int)p.x;
ev->y = (int)p.y;
ev->amount = amount;
}
void create_explosion(vec2 p, int owner, bool bnodamage)
{
// create the event
ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion));
ev->x = (int)p.x;
ev->y = (int)p.y;
if (!bnodamage)
{
// deal damage
entity *ents[64];
const float radius = 128.0f;
int num = world.find_entities(p, radius, ents, 64);
for(int i = 0; i < num; i++)
{
vec2 diff = ents[i]->pos - p;
vec2 forcedir(0,1);
if (length(diff))
forcedir = normalize(diff);
float l = length(diff);
float dmg = 5 * (1 - (l/radius));
if((int)dmg)
{
ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner);/* &&
ents[i]->objtype == OBJTYPE_PLAYER &&
owner >= 0)
{
player *p = (player*)ents[i];
if(p->client_id == owner)
p->score--;
else
((player*)ents[owner])->score++;
}*/
}
}
}
}
void create_smoke(vec2 p)
{
// create the event
ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion));
ev->x = (int)p.x;
ev->y = (int)p.y;
}
void create_sound(vec2 pos, int sound, int loopingflags)
{
if (sound < 0)
return;
// create a sound
ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound));
ev->x = (int)pos.x;
ev->y = (int)pos.y;
ev->sound = sound | loopingflags;
}
player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
{
// Find other players
entity *ents[64];
vec2 dir = pos1 - pos0;
float radius = length(dir * 0.5f);
vec2 center = pos0 + dir * 0.5f;
int num = world.find_entities(center, radius, ents, 64);
for (int i = 0; i < num; i++)
{
// Check if entity is a player
if (ents[i] != notthis && ents[i]->objtype == OBJTYPE_PLAYER)
{
// temp, set hook pos to our position
new_pos = ents[i]->pos;
return (player*)ents[i];
}
}
return 0;
}
// Server hooks
static int addtick = SERVER_TICK_SPEED * 5;
void mods_tick()
{
// clear all events
events.clear();
world.tick();
if (addtick <= 0)
{
powerup::spawnrandom(SERVER_TICK_SPEED * 5);
addtick = SERVER_TICK_SPEED * 5;
}
addtick--;
}
void mods_snap(int client_id)
{
world.snap(client_id);
events.snap(client_id);
}
void mods_client_input(int client_id, void *input)
{
players[client_id].previnput = players[client_id].input;
players[client_id].input = *(player_input*)input;
}
void mods_client_enter(int client_id)
{
players[client_id].reset();
players[client_id].client_id = client_id;
players[client_id].respawn();
world.insert_entity(&players[client_id]);
}
void mods_client_drop(int client_id)
{
players[client_id].client_id = -1;
world.remove_entity(&players[client_id]);
}
void mods_init()
{
col_init(32);
int start, num;
map_get_type(MAPRES_ITEM, &start, &num);
for(int i = 0; i < num; i++)
{
mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0);
int type = -1;
int subtype = -1;
int numitems = 1;
switch(it->type)
{
case ITEM_WEAPON_GUN:
type = POWERUP_TYPE_WEAPON;
subtype = WEAPON_TYPE_GUN;
break;
case ITEM_WEAPON_SHOTGUN:
type = POWERUP_TYPE_WEAPON;
subtype = WEAPON_TYPE_SHOTGUN;
numitems = 5;
break;
case ITEM_WEAPON_ROCKET:
type = POWERUP_TYPE_WEAPON;
subtype = WEAPON_TYPE_ROCKET;
numitems = 5;
break;
/*case ITEM_WEAPON_SNIPER:
type = POWERUP_TYPE_WEAPON;
subtype = WEAPON_TYPE_ROCKET;
break;*/
case ITEM_WEAPON_HAMMER:
type = POWERUP_TYPE_WEAPON;
subtype = WEAPON_TYPE_MELEE;
break;
case ITEM_HEALTH_1:
type = POWERUP_TYPE_HEALTH;
numitems = 1;
break;
case ITEM_HEALTH_5:
type = POWERUP_TYPE_HEALTH;
numitems = 5;
break;
case ITEM_HEALTH_10:
type = POWERUP_TYPE_HEALTH;
numitems = 10;
break;
case ITEM_ARMOR_1:
type = POWERUP_TYPE_ARMOR;
numitems = 1;
break;
case ITEM_ARMOR_5:
type = POWERUP_TYPE_ARMOR;
numitems = 5;
break;
case ITEM_ARMOR_10:
type = POWERUP_TYPE_ARMOR;
numitems = 10;
break;
};
powerup* ppower = new powerup(type, subtype, numitems);
ppower->pos.x = it->x;
ppower->pos.y = it->y;
}
/*
powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan);
ppower->pos = pos;
if (ppower->type == POWERUP_TYPE_WEAPON)
{
ppower->subtype = rand() % WEAPON_NUMWEAPONS;
ppower->numitems = 10;
}
else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR)
{
ppower->numitems = rand() % 5;
}
ppower->flags |= POWERUP_FLAG_HOOKABLE;
*/
//powerup::spawnrandom(SERVER_TICK_SPEED * 5);
}
void mods_shutdown() {}
void mods_presnap() {}
void mods_postsnap() {}