diff options
| author | Magnus Auvinen <magnus.auvinen@gmail.com> | 2007-05-22 15:03:32 +0000 |
|---|---|---|
| committer | Magnus Auvinen <magnus.auvinen@gmail.com> | 2007-05-22 15:03:32 +0000 |
| commit | 73aa9b71c1d8b5c5065d1e474f13601da3ca6b20 (patch) | |
| tree | 88b6a0a4a2ebdd33a88f4a25682581d329d33f6b /src/game | |
| download | zcatch-73aa9b71c1d8b5c5065d1e474f13601da3ca6b20.tar.gz zcatch-73aa9b71c1d8b5c5065d1e474f13601da3ca6b20.zip | |
started the major restructure of svn
Diffstat (limited to 'src/game')
| -rw-r--r-- | src/game/game.h | 231 | ||||
| -rw-r--r-- | src/game/game_client.cpp | 1992 | ||||
| -rw-r--r-- | src/game/game_server.cpp | 2122 | ||||
| -rw-r--r-- | src/game/mapres.h | 7 | ||||
| -rw-r--r-- | src/game/mapres_col.cpp | 44 | ||||
| -rw-r--r-- | src/game/mapres_col.h | 10 | ||||
| -rw-r--r-- | src/game/mapres_image.cpp | 41 | ||||
| -rw-r--r-- | src/game/mapres_image.h | 18 | ||||
| -rw-r--r-- | src/game/mapres_tilemap.cpp | 54 | ||||
| -rw-r--r-- | src/game/mapres_tilemap.h | 19 |
10 files changed, 4538 insertions, 0 deletions
diff --git a/src/game/game.h b/src/game/game.h new file mode 100644 index 00000000..a1817eea --- /dev/null +++ b/src/game/game.h @@ -0,0 +1,231 @@ +#include <baselib/system.h> +#include <baselib/vmath.h> +#include <math.h> +#include "../interface.h" +#include "mapres_col.h" + +// Don't tweak :) +const float pi = 3.14159265358979f; + +#define LERP(a,b,t) (a + (b-a) * t) +#define min(a, b) ( a > b ? b : a) +#define max(a, b) ( a > b ? a : b) + +inline baselib::vec2 get_direction(int angle) +{ + float a = angle/256.0f; + return baselib::vec2(cosf(a), sinf(a)); +} + +inline float get_angle(baselib::vec2 dir) +{ + float a = atan(dir.y/dir.x); + if(dir.x < 0) + a = a+pi; + return a; +} + +inline bool col_check_point(float x, float y) { return col_check_point((int)x, (int)y); } +inline bool col_check_point(baselib::vec2 p) { return col_check_point(p.x, p.y); } + +// Network stuff + +enum +{ + OBJTYPE_NULL=0, + OBJTYPE_PLAYER, + OBJTYPE_PROJECTILE, + OBJTYPE_POWERUP, + EVENT_EXPLOSION, + EVENT_HEALTHMOD, + EVENT_SOUND, + EVENT_SMOKE, +}; + +enum +{ + EMOTE_NORMAL=0, + EMOTE_BLINK, + EMOTE_WINK, + EMOTE_PAIN, + EMOTE_HAPPY, +}; + +struct player_input +{ + int left; + int right; + int angle; + int jump; + int fire; + int hook; + int blink; + int activeweapon; +}; + +struct ev_explosion +{ + int x, y; +}; + +struct ev_sound +{ + int x, y; + int sound; // if (0x80000000 flag is set -> looping) if (0x40000000 is set -> stop looping +}; + +struct ev_healthmod +{ + int x, y; + int amount; +}; + +struct obj_projectile +{ + int type; + int x, y; + int vx, vy; +}; + +struct obj_powerup +{ + int type; + int subtype; + int x, y; + int vx, vy; +}; + +struct obj_player +{ + int name[8]; + + int local; + int clientid; + + int health; + int armor; + int ammocount; + + int x, y; + int vx, vy; + int angle; + + // current active weapon + int weapon; + // current active modifier + int modifier; + + // num attack ticks left of current attck + int attackticks; + int attacklen; + int visualtimeattack; + + int score; + int emote; + + int hook_active; + int hook_x, hook_y; +}; + +enum +{ + WEAPON_TYPE_GUN = 0, + WEAPON_TYPE_ROCKET = 1, + WEAPON_TYPE_SHOTGUN = 2, + WEAPON_TYPE_MELEE = 3, + WEAPON_NUMWEAPONS, + //WEAPON_TYPE_SNIPER = 2, + + POWERUP_TYPE_HEALTH = 0, + POWERUP_TYPE_ARMOR = 1, + POWERUP_TYPE_WEAPON = 2, + POWERUP_TYPE_NINJA = 3, + POWERUP_TYPE_TIMEFIELD = 4, + POWERUP_TYPE_NUMPOWERUPS, + + PLAYER_MAXHEALTH = 10, + PLAYER_MAXARMOR = 10, + + MODIFIER_TYPE_NINJA = 0, + MODIFIER_TYPE_TIMEFIELD = 1, + MODIFIER_NUMMODIFIERS, +}; + + +struct mapres_spawnpoint +{ + int x, y; + int type; +}; + +struct mapres_item +{ + int x, y; + int type; +}; + +enum +{ + MAPRES_SPAWNPOINT=1, + MAPRES_ITEM=2, + + ITEM_NULL=0, + ITEM_WEAPON_GUN=0x00010001, + ITEM_WEAPON_SHOTGUN=0x00010002, + ITEM_WEAPON_ROCKET=0x00010003, + ITEM_WEAPON_SNIPER=0x00010004, + ITEM_WEAPON_HAMMER=0x00010005, + ITEM_HEALTH_1 =0x00020001, + ITEM_HEALTH_5 =0x00020005, + ITEM_HEALTH_10=0x00020010, + ITEM_ARMOR_1=0x00030001, + ITEM_ARMOR_5=0x00030005, + ITEM_ARMOR_10=0x00030010, + ITEM_NINJA=0x00040001, +}; + +// sound categories and stuff +enum +{ + SOUND_FIRE_GUN = 0, + SOUND_FIRE_SHOTGUN, + SOUND_FIRE_ROCKET, + SOUND_FIRE_MELEE, + SOUND_FIRE_NINJA, + + + // impacts with world + SOUND_IMPACT_PROJECTILE_GUN, + SOUND_IMPACT_PROJECTILE_SHOTGUN, + SOUND_IMPACT_PROJECTILE_ROCKET, + + + // chain ? + + // Player movement + SOUND_PLAYER_JUMP, + SOUND_PLAYER_HURT_SHORT, + SOUND_PLAYER_HURT_LONG, + SOUND_PLAYER_SPAWN, + SOUND_PLAYER_CHAIN_LOOP, + SOUND_PLAYER_CHAIN_IMPACT, + SOUND_PLAYER_IMPACT, + SOUND_PLAYER_IMPACT_NINJA, + SOUND_PLAYER_DIE, + SOUND_PLAYER_SWITCHWEAPON, + SOUND_PLAYER_EQUIP, + SOUND_PLAYER_LAND, + + SOUND_NUMSOUNDS, + + + // extra defs (for future?) + SOUND_EQUIP_GUN = SOUND_PLAYER_EQUIP, + SOUND_EQUIP_ROCKET = SOUND_PLAYER_EQUIP, + SOUND_EQUIP_SHOTGUN = SOUND_PLAYER_EQUIP, + SOUND_EQUIP_MELEE = SOUND_PLAYER_EQUIP, + + SOUND_LOOPFLAG_STARTLOOP = 0x80000000, + SOUND_LOOPFLAG_STOPLOOP = 0x40000000, + SOUND_MASK = ~(SOUND_LOOPFLAG_STARTLOOP | SOUND_LOOPFLAG_STOPLOOP), +}; diff --git a/src/game/game_client.cpp b/src/game/game_client.cpp new file mode 100644 index 00000000..6e431591 --- /dev/null +++ b/src/game/game_client.cpp @@ -0,0 +1,1992 @@ +#include <stdlib.h> +#include <stdio.h> +#include "game.h" +#include "mapres_image.h" +#include "mapres_tilemap.h" + +using namespace baselib; + +static int texture_char_default = 0; +static int texture_game = 0; +static int texture_weapon = 0; +static int texture_sun = 0; +static int texture_particles = 0; + +struct weapontexcell +{ + float x; + float y; + float w; + float h; +}; +struct renderparams +{ + float sizex; + float sizey; + float offsetx; + float offsety; +}; +int numcellsx = 32; +int numcellsy = 32; +renderparams weaponrenderparams[WEAPON_NUMWEAPONS]; +renderparams modifierrenderparams[WEAPON_NUMWEAPONS]; + +weapontexcell weaponprojtexcoord[WEAPON_NUMWEAPONS]; +weapontexcell weapontexcoord[WEAPON_NUMWEAPONS]; +weapontexcell weapontexcoordcursor[WEAPON_NUMWEAPONS]; + +weapontexcell poweruptexcoord[POWERUP_TYPE_NUMPOWERUPS]; + +weapontexcell modifiertexcoord[MODIFIER_NUMMODIFIERS]; +weapontexcell modifiertexcoordcursor[MODIFIER_NUMMODIFIERS]; + +int nummuzzletex[WEAPON_NUMWEAPONS]; +weapontexcell muzzletexcoord[WEAPON_NUMWEAPONS][3]; +renderparams muzzleparams[WEAPON_NUMWEAPONS]; + +#define NUMHADOKENS 6 +#define NUMSTARS 2 +#define NUMPARTICLES 9 +int particlesnumcellsx = 16; +int particlesnumcellsy = 16; +weapontexcell chaintexcoord; +weapontexcell chainheadtexcoord; +weapontexcell stars[NUMSTARS]; + +float lifemodifier[NUMPARTICLES]; +vec4 particlecolors[NUMPARTICLES]; +weapontexcell particlestexcoord[NUMPARTICLES]; + +int charnumcellsx = 8; +int charnumcellsy = 32; +int charoffsety = 2; +weapontexcell body[2]; +weapontexcell leye; +weapontexcell reye; +weapontexcell feet[2]; + +int charids[16] = { 2,10,0,4,12,6,14,1,9,15,13,11,7,5,8,3 }; + +renderparams hadokenparams[6]; +weapontexcell hadoken[6]; + +float recoils[WEAPON_NUMWEAPONS] = { 10.0f, 10.0f, 10.0f, 10.0f }; + +static int font_texture = 0; +static vec2 mouse_pos; + + +static vec2 local_player_pos; +static obj_player *local_player; + +float frandom() +{ + return rand()/(float)(RAND_MAX); +} + +float sign(float f) +{ + return f<0.0f?-1.0f:1.0f; +} + +// sound helpers +template<int N> +class sound_kit +{ +private: + int sounds[N]; + int last_id; +public: + sound_kit() : last_id(-1) { } + + int& operator[](int id) { return sounds[id]; } + + inline void play_random(float vol = 1.0f, float pan = 0.0f); +}; + +template<> +inline void sound_kit<1>::play_random(float vol, float pan) +{ + snd_play(sounds[0], SND_PLAY_ONCE, vol, pan); +} + +template<int N> +inline void sound_kit<N>::play_random(float vol, float pan) +{ + int id; + do { + id = rand() % N; + } while(id == last_id); + snd_play(sounds[id], SND_PLAY_ONCE, vol, pan); + last_id = id; +} + + +// sound volume tweak +static const float stereo_separation = 0.01f; +static const float stereo_separation_deadzone = 512.0f; +static const float volume_distance_falloff = 100.0f; +static const float volume_distance_deadzone = 512.0f; +static const float volume_gun = 0.5f; +static const float volume_tee = 0.5f; +static const float volume_hit = 0.5f; +static const float volume_music = 0.8f; + +// sounds +sound_kit<3> sound_gun_fire; +sound_kit<3> sound_shotty_fire; +sound_kit<3> sound_flump_launch; +sound_kit<3> sound_hammer_swing; +sound_kit<3> sound_ninja_attack; + +sound_kit<3> sound_flump_explode; +sound_kit<4> sound_ninja_hit; + +sound_kit<3> sound_weapon_switch; + +sound_kit<12> sound_pain_short; +sound_kit<2> sound_pain_long; + +sound_kit<4> sound_body_jump; +sound_kit<4> sound_body_land; +sound_kit<2> sound_body_splat; + +sound_kit<7> sound_spawn; +sound_kit<2> sound_tee_cry; + +sound_kit<1> sound_hook_loop; +sound_kit<3> sound_hook_attach; + +void sound_vol_pan(const vec2& p, float *vol, float *pan) +{ + vec2 player_to_ev = p - local_player_pos; + *pan = 0.0f; + *vol = 1.0f; + + if(abs(player_to_ev.x) > stereo_separation_deadzone) + { + *pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone); + if(*pan < -1.0f) *pan = -1.0f; + if(*pan > 1.0f) *pan = 1.0f; + } + + float len = length(player_to_ev); + if(len > volume_distance_deadzone) + { + *vol = volume_distance_falloff / (len - volume_distance_deadzone); + + if(*vol < 0.0f) *vol = 0.0f; + if(*vol > 1.0f) *vol = 1.0f; + } +} + +// TODO: we should do something nicer then this +static void cell_select_ex(int cx, int cy, float x, float y, float w, float h) +{ + gfx_quads_setsubset(x/(float)cx,y/(float)cy,(x+w)/(float)cx,(y+h)/(float)cy); +} + +static void cell_select_ex_flip_x(int cx, int cy, float x, float y, float w, float h) +{ + gfx_quads_setsubset((x+w)/(float)cx,y/(float)cy,x /(float)cx,(y+h)/(float)cy); +} + +static void cell_select_ex_flip_y(int cx, int cy, float x, float y, float w, float h) +{ + gfx_quads_setsubset(x/(float)cx,(y+h)/(float)cy,(x+w)/(float)cx,y/(float)cy); +} + +static void cell_select(int x, int y, int w, int h) +{ + gfx_quads_setsubset(x/16.0f,y/16.0f,(x+w)/16.0f,(y+h)/16.0f); +} + +inline void cell_select_flip_x(int x, int y, int w, int h) +{ + gfx_quads_setsubset((x+w)/16.0f,y/16.0f,(x)/16.0f,(y+h)/16.0f); +} + +inline void cell_select_flip_y(int x, int y, int w, int h) +{ + gfx_quads_setsubset(x/16.0f,(y+h)/16.0f,(x+w)/16.0f,(y)/16.0f); +} + +struct particle +{ + vec2 pos; + vec2 vel; + float life; + float max_life; + float size; + + float rot; + float rotspeed; + + float gravity; + float friction; + int iparticle; + + vec4 color; +}; + +void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity) +{ + vec2 pos = *inout_pos; + vec2 vel = *inout_vel; + if(col_check_point(pos + vel)) + { + int affected = 0; + if(col_check_point(pos.x + vel.x, pos.y)) + { + inout_vel->x *= -elasticity; + affected++; + } + + if(col_check_point(pos.x, pos.y + vel.y)) + { + inout_vel->y *= -elasticity; + affected++; + } + + if(affected == 0) + { + inout_vel->x *= -elasticity; + inout_vel->y *= -elasticity; + } + } + else + { + *inout_pos = pos + vel; + } +} + +class health_texts +{ +public: + int64 lastupdate; + struct item + { + vec2 pos; + vec2 vel; + int amount; + int istar; + float life; + float startangle; + }; + + enum + { + MAX_ITEMS=16, + }; + + health_texts() + { + lastupdate = 0; + } + + item items[MAX_ITEMS]; + int num_items; + + item *create_i() + { + if (num_items < MAX_ITEMS) + { + item *p = &items[num_items]; + num_items++; + return p; + } + return 0; + } + + void destroy_i(item *i) + { + num_items--; + *i = items[num_items]; + } + + void create(vec2 pos, int amount) + { + amount = max(1,amount); + for (int j = 0; j < amount; j++) + { + float a = j/(float)amount-0.5f; + item *i = create_i(); + if (i) + { + i->pos = pos; + i->pos.y -= 20.0f; + i->pos.x += ((float)rand()/(float)RAND_MAX) * 5.0f; + i->amount = amount; + i->life = 1.5f; + i->istar = rand() % NUMSTARS; + i->vel = vec2(((float)rand()/(float)RAND_MAX) * 50.0f,-150.0f); + i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi; + } + } + } + + void render() + { + if (!lastupdate) + lastupdate = time_get(); + + int64 lasttime = lastupdate; + lastupdate = time_get(); + + float delta = (float) (lastupdate - lasttime) / (float)time_freq(); + gfx_texture_set(texture_particles); + gfx_quads_begin(); + for(int i = 0; i < num_items;) + { + items[i].vel += vec2(0,500.0f) * delta; + items[i].pos += items[i].vel * delta; + items[i].life -= delta; + //items[i].pos.y -= frametime*15.0f; + if(items[i].life < 0.0f) + destroy_i(&items[i]); + else + { + gfx_quads_setcolor(1.0f,1.0f,1.0f, items[i].life / 1.5f); + gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f); + float size = 64.0f; + cell_select_ex(particlesnumcellsx,particlesnumcellsy, stars[items[i].istar].x,stars[items[i].istar].y, stars[items[i].istar].w, stars[items[i].istar].h); + gfx_quads_draw(items[i].pos.x-size/2, items[i].pos.y-size/2, size, size); + /*char buf[32]; + if(items[i].amount < 0) + { + sprintf(buf, "%d", items[i].amount*-1); + } + else + { + sprintf(buf, "%d", items[i].amount); + } + float size = 42.0f; + if(items[i].life > 1.25f) + size += 42.0f * ((items[i].life - 1.25f) * 4); + gfx_quads_text(items[i].pos.x-size/2, items[i].pos.y, size, buf);*/ + i++; + } + } + gfx_quads_end(); + } + +}; + +/*class texture_animator +{ +public: + + int texture; + int numframes; + float duration; + float* framereltime; + weapontexcell* params; + texture_animator() + { + texture = -1; + numframes = 0; + duration = 0; + framereltime = 0; + params = 0; + } + + ~texture_animator() + { + if (params) + mem_free(params); + if (framereltime) + mem_free(framereltime); + } + + void create_anim(int texture, int numframes, float duration) + { + framereltime = 0; + params = 0; + this->texture = texture; + this->numframes = numframes; + this->duration = duration; + if (numframes) + { + framereltime = (float*)mem_alloc(sizeof(float) * numframes,1); + params = (weapontexcell*)mem_alloc(sizeof(renderparams) * numframes,1); + float delta = 1.0f / (float)(numframes - 1); + for (int i = 0; i < numframes; i++) + { + framereltime[i] = delta * i; + } + } + } + + static void create_gunmuzzle(texture_animator& anim, int texture, float duration) + { + anim.create_anim(texture, 3, duration); + anim.params[0].x = 8; + anim.params[0].y = 4; + anim.params[0].w = 3; + anim.params[0].h = 2; + anim.params[1].x = 12; + anim.params[1].y = 4; + anim.params[1].w = 3; + anim.params[1].h = 2; + anim.params[2].x = 16; + anim.params[2].y = 4; + anim.params[2].w = 3; + anim.params[2].h = 2; + } + + static void create_shotgunmuzzle() + { + + } +};*/ + +class keyframe +{ +public: + vec2 pos; + float angle; + float relativetime; +}; + +class anim +{ +public: + keyframe* keyframes; + int numframes; + float duration; + anim() + { + numframes = 0; + keyframes = 0; + } + ~anim() + { + if (keyframes) + mem_free(keyframes); + } + + void create_anim(int numframes, float duration) + { + if (keyframes) + mem_free(keyframes); + + this->numframes = numframes; + this->duration = duration; + keyframes = (keyframe*)mem_alloc(sizeof(keyframe) * numframes,1); + float delta = 1.0f / (float) (numframes - 1); + for (int i = 0; i < numframes; i++) + { + keyframes[i].pos = vec2(0.0f,0.0f); + keyframes[i].angle = 0; + keyframes[i].relativetime = delta * (float)i; + } + } + + void getframes(float relativetime, keyframe*& frame1, keyframe*& frame2, float& blend) + { + for (int i = 1; i < numframes; i++) + { + if (keyframes[i-1].relativetime <= relativetime && keyframes[i].relativetime >= relativetime) + { + frame1 = &keyframes[i-1]; + frame2 = &keyframes[i]; + blend = (relativetime - frame1->relativetime) / (frame2->relativetime - frame1->relativetime); + } + } + } + + void evalanim(float time, vec2& pos, float& angle) + { + float reltime = max(0.0f, min(1.0f, time / duration)); + keyframe* frame1 = 0; + keyframe* frame2 = 0; + float blend = 0.0f; + getframes(reltime, frame1, frame2, blend); + + if (frame1 && frame2) + { + pos = mix(frame1->pos, frame2->pos, blend); + angle = LERP(frame1->angle, frame2->angle, blend); + } + } + + static void setup_hammer(anim& hammeranim) + { + // straight up = -0.25 + // frame0 = standard pose time 0 + // frame1 = back a little time 0.3 + // frame2 = over head time 0.4 + // frame3 = on ground smashed time 0.5 + // frame4 = back to standard pose time 1.0 + hammeranim.create_anim(5, 1.0f); + // only angles... (for now...) + hammeranim.keyframes[0].angle = -0.35f * pi * 2.0f; + hammeranim.keyframes[1].angle = -0.4f * pi * 2.0f; + hammeranim.keyframes[1].relativetime = 0.3f; + hammeranim.keyframes[2].angle = -0.25f; + hammeranim.keyframes[2].relativetime = 0.4f; + hammeranim.keyframes[3].angle = 0.0f * pi * 2.0f; + hammeranim.keyframes[3].relativetime = 0.5f; + hammeranim.keyframes[4].angle = -0.35f * pi * 2.0f; + hammeranim.keyframes[4].relativetime = 1.0f; + } + + static void setup_ninja(anim& ninjanim) + { + // (straight up = -0.25) + // frame0 = standard pose straight back time 0.0 + // frame1 = overhead attack frame 1 time 0.1 + // frame2 = attack end frame time 0.15 + // frame3 = attack hold frame (a bit up) time 0.4 + // frame4 = attack hold frame end time 0.7 + // frame5 = endframe time 1.0 + ninjanim.create_anim(6, 1.0f); + // only angles... (for now...) + ninjanim.keyframes[0].angle = -0.5f * pi * 2.0f; + + ninjanim.keyframes[1].angle = -0.3f * pi * 2.0f; + ninjanim.keyframes[1].relativetime = 0.1f; + + ninjanim.keyframes[2].angle = 0.1f * pi * 2.0f; + ninjanim.keyframes[2].relativetime = 0.15f; + + ninjanim.keyframes[3].angle = -0.05f * pi * 2.0f; + ninjanim.keyframes[3].relativetime = 0.42; + + ninjanim.keyframes[4].angle = -0.05f * pi * 2.0f; + ninjanim.keyframes[4].relativetime = 0.5f; + + ninjanim.keyframes[5].angle = -0.5f * pi * 2.0f; + ninjanim.keyframes[5].relativetime = 1.0f; + } +}; + +static anim hammeranim; +static anim ninjaanim; +static health_texts healthmods; + +class particle_system +{ +public: + enum + { + MAX_PARTICLES=1024, + }; + + particle particles[MAX_PARTICLES]; + int num_particles; + + particle_system() + { + num_particles = 0; + } + + void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction) + { + if (num_particles >= MAX_PARTICLES) + return; + + particles[num_particles].iparticle = rand() % NUMPARTICLES; + particles[num_particles].pos = pos; + particles[num_particles].vel = vel; + particles[num_particles].life = life - lifemodifier[particles[num_particles].iparticle] * life; + particles[num_particles].size = size; + particles[num_particles].max_life = life; + particles[num_particles].gravity = gravity; + particles[num_particles].friction = friction; + particles[num_particles].rot = frandom()*pi*2; + particles[num_particles].rotspeed = frandom() * 10.0f; + num_particles++; + } + + void update(float time_passed) + { + for(int i = 0; i < num_particles; i++) + { + particles[i].vel.y += particles[i].gravity*time_passed; + particles[i].vel *= particles[i].friction; + vec2 vel = particles[i].vel*time_passed; + move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom()); + particles[i].vel = vel* (1.0f/time_passed); + particles[i].life += time_passed; + particles[i].rot += time_passed * particles[i].rotspeed; + + // check particle death + if(particles[i].life > particles[i].max_life) + { + num_particles--; + particles[i] = particles[num_particles]; + i--; + } + } + } + + void render() + { + gfx_blend_additive(); + gfx_texture_set(texture_particles); + gfx_quads_begin(); + //cell_select(4,1,1,1); + //cell_select(0,6,2,2); + //gfx_quads_setrotation(get_angle(vec2(proj->vx, proj->vy))); + for(int i = 0; i < num_particles; i++) + { + int type = particles[i].iparticle; + cell_select_ex(particlesnumcellsx,particlesnumcellsy,particlestexcoord[type].x, particlestexcoord[type].y, particlestexcoord[type].w, particlestexcoord[type].h); + float a = 1 - particles[i].life / particles[i].max_life; + vec2 p = particles[i].pos; + //a *= length(particles[i].vel) * 0.01f; + gfx_quads_setrotation(particles[i].rot); + gfx_quads_setcolor(particlecolors[type].x,particlecolors[type].y,particlecolors[type].z,pow(a,0.75f)); + //gfx_quads_setcolor(particlecolors[type].x * 0.5,particlecolors[type].y * 0.5,particlecolors[type].z* 0.5,pow(a,0.75f)); + //gfx_quads_setcolor(particlecolors[type].x * 0.0,particlecolors[type].y * 0.0,particlecolors[type].z* 0.0,pow(a,0.75f)); + //gfx_quads_setcolor(0.64f*2,0.28f*2,0.16f*2,pow(a,0.75f)); + gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size); + } + gfx_quads_end(); + gfx_blend_normal(); + } +}; + +static particle_system temp_system; + +void modc_init() +{ + // load textures + texture_weapon = gfx_load_texture_tga("data/tileset_weapons.tga"); + texture_game = gfx_load_texture_tga("data/game_main.tga"); + texture_char_default = gfx_load_texture_tga("data/char_teefault.tga"); + texture_sun = gfx_load_texture_tga("data/sun.tga"); + texture_particles = gfx_load_texture_tga("data/tileset_particles.tga"); + font_texture = gfx_load_texture_tga("data/debug_font.tga"); + + + // load sounds + sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav"); + sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav"); + sound_gun_fire[1] = snd_load_wav("data/audio/wp_gun_fire-02.wav"); + sound_shotty_fire[0] = snd_load_wav("data/audio/wp_shotty_fire-01.wav"); + sound_shotty_fire[1] = snd_load_wav("data/audio/wp_shotty_fire-02.wav"); + sound_shotty_fire[2] = snd_load_wav("data/audio/wp_shotty_fire-03.wav"); + sound_flump_launch[0] = snd_load_wav("data/audio/wp_flump_launch-01.wav"); + sound_flump_launch[1] = snd_load_wav("data/audio/wp_flump_launch-02.wav"); + sound_flump_launch[2] = snd_load_wav("data/audio/wp_flump_launch-03.wav"); + sound_hammer_swing[0] = snd_load_wav("data/audio/wp_hammer_swing-01.wav"); + sound_hammer_swing[1] = snd_load_wav("data/audio/wp_hammer_swing-02.wav"); + sound_hammer_swing[2] = snd_load_wav("data/audio/wp_hammer_swing-03.wav"); + sound_ninja_attack[0] = snd_load_wav("data/audio/wp_ninja_attack-01.wav"); + sound_ninja_attack[1] = snd_load_wav("data/audio/wp_ninja_attack-02.wav"); + sound_ninja_attack[2] = snd_load_wav("data/audio/wp_ninja_attack-03.wav"); + + sound_flump_explode[0] = snd_load_wav("data/audio/wp_flump_explo-01.wav"); + sound_flump_explode[1] = snd_load_wav("data/audio/wp_flump_explo-02.wav"); + sound_flump_explode[2] = snd_load_wav("data/audio/wp_flump_explo-03.wav"); + sound_ninja_hit[0] = snd_load_wav("data/audio/wp_ninja_hit-01.wav"); + sound_ninja_hit[1] = snd_load_wav("data/audio/wp_ninja_hit-02.wav"); + sound_ninja_hit[2] = snd_load_wav("data/audio/wp_ninja_hit-03.wav"); + sound_ninja_hit[3] = snd_load_wav("data/audio/wp_ninja_hit-04.wav"); + + sound_weapon_switch[0] = snd_load_wav("data/audio/wp_switch-01.wav"); + sound_weapon_switch[1] = snd_load_wav("data/audio/wp_switch-02.wav"); + sound_weapon_switch[2] = snd_load_wav("data/audio/wp_switch-03.wav"); + + sound_pain_short[0] = snd_load_wav("data/audio/vo_teefault_pain_short-01.wav"); + sound_pain_short[1] = snd_load_wav("data/audio/vo_teefault_pain_short-02.wav"); + sound_pain_short[2] = snd_load_wav("data/audio/vo_teefault_pain_short-03.wav"); + sound_pain_short[3] = snd_load_wav("data/audio/vo_teefault_pain_short-04.wav"); + sound_pain_short[4] = snd_load_wav("data/audio/vo_teefault_pain_short-05.wav"); + sound_pain_short[5] = snd_load_wav("data/audio/vo_teefault_pain_short-06.wav"); + sound_pain_short[6] = snd_load_wav("data/audio/vo_teefault_pain_short-07.wav"); + sound_pain_short[7] = snd_load_wav("data/audio/vo_teefault_pain_short-08.wav"); + sound_pain_short[8] = snd_load_wav("data/audio/vo_teefault_pain_short-09.wav"); + sound_pain_short[9] = snd_load_wav("data/audio/vo_teefault_pain_short-10.wav"); + sound_pain_short[10] = snd_load_wav("data/audio/vo_teefault_pain_short-11.wav"); + sound_pain_short[11] = snd_load_wav("data/audio/vo_teefault_pain_short-12.wav"); + + sound_pain_long[0] = snd_load_wav("data/audio/vo_teefault_pain_long-01.wav"); + sound_pain_long[1] = snd_load_wav("data/audio/vo_teefault_pain_long-02.wav"); + + sound_body_land[0] = snd_load_wav("data/audio/foley_land-01.wav"); + sound_body_land[1] = snd_load_wav("data/audio/foley_land-02.wav"); + sound_body_land[2] = snd_load_wav("data/audio/foley_land-03.wav"); + sound_body_land[3] = snd_load_wav("data/audio/foley_land-04.wav"); + sound_body_jump[0] = snd_load_wav("data/audio/foley_foot_left-01.wav"); + sound_body_jump[1] = snd_load_wav("data/audio/foley_foot_left-02.wav"); + sound_body_jump[2] = snd_load_wav("data/audio/foley_foot_left-03.wav"); + sound_body_jump[3] = snd_load_wav("data/audio/foley_foot_left-04.wav"); + sound_body_jump[4] = snd_load_wav("data/audio/foley_foot_right-01.wav"); + sound_body_jump[5] = snd_load_wav("data/audio/foley_foot_right-02.wav"); + sound_body_jump[6] = snd_load_wav("data/audio/foley_foot_right-03.wav"); + sound_body_jump[7] = snd_load_wav("data/audio/foley_foot_right-04.wav"); + + sound_body_splat[1] = snd_load_wav("data/audio/foley_body_splat-02.wav"); + sound_body_splat[2] = snd_load_wav("data/audio/foley_body_splat-03.wav"); + sound_body_splat[3] = snd_load_wav("data/audio/foley_body_splat-04.wav"); + + sound_spawn[0] = snd_load_wav("data/audio/vo_teefault_spawn-01.wav"); + sound_spawn[1] = snd_load_wav("data/audio/vo_teefault_spawn-02.wav"); + sound_spawn[2] = snd_load_wav("data/audio/vo_teefault_spawn-03.wav"); + sound_spawn[3] = snd_load_wav("data/audio/vo_teefault_spawn-04.wav"); + sound_spawn[4] = snd_load_wav("data/audio/vo_teefault_spawn-05.wav"); + sound_spawn[5] = snd_load_wav("data/audio/vo_teefault_spawn-06.wav"); + sound_spawn[6] = snd_load_wav("data/audio/vo_teefault_spawn-07.wav"); + + sound_tee_cry[0] = snd_load_wav("data/audio/vo_teefault_cry-01.wav"); + sound_tee_cry[1] = snd_load_wav("data/audio/vo_teefault_cry-02.wav"); + + //sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-01.wav"); + sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-02.wav"); + sound_hook_attach[0] = snd_load_wav("data/audio/hook_attach-01.wav"); + sound_hook_attach[1] = snd_load_wav("data/audio/hook_attach-02.wav"); + sound_hook_attach[2] = snd_load_wav("data/audio/hook_attach-03.wav"); + + poweruptexcoord[POWERUP_TYPE_HEALTH].x = 10; + poweruptexcoord[POWERUP_TYPE_HEALTH].y = 2; + poweruptexcoord[POWERUP_TYPE_HEALTH].w = 2; + poweruptexcoord[POWERUP_TYPE_HEALTH].h = 2; + + poweruptexcoord[POWERUP_TYPE_ARMOR].x = 12; + poweruptexcoord[POWERUP_TYPE_ARMOR].y = 2; + poweruptexcoord[POWERUP_TYPE_ARMOR].w = 2; + poweruptexcoord[POWERUP_TYPE_ARMOR].h = 2; + + poweruptexcoord[POWERUP_TYPE_WEAPON].x = 3; + poweruptexcoord[POWERUP_TYPE_WEAPON].y = 0; + poweruptexcoord[POWERUP_TYPE_WEAPON].w = 6; + poweruptexcoord[POWERUP_TYPE_WEAPON].h = 2; + + poweruptexcoord[POWERUP_TYPE_NINJA].x = 3; + poweruptexcoord[POWERUP_TYPE_NINJA].y = 10; + poweruptexcoord[POWERUP_TYPE_NINJA].w = 7; + poweruptexcoord[POWERUP_TYPE_NINJA].h = 2; + + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].x = 3; + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].y = 0; + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].w = 6; + poweruptexcoord[POWERUP_TYPE_TIMEFIELD].h = 2; + + // Setup weapon cell coords + float sizemodifier = 1.0f; + weaponrenderparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_GUN].sizey = 30.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_GUN].offsetx = 32.0f; + weaponrenderparams[WEAPON_TYPE_GUN].offsety = 4.0f; + weapontexcoordcursor[WEAPON_TYPE_GUN].x = 0; + weapontexcoordcursor[WEAPON_TYPE_GUN].y = 4; + weapontexcoordcursor[WEAPON_TYPE_GUN].w = 2; + weapontexcoordcursor[WEAPON_TYPE_GUN].h = 2; + weapontexcoord[WEAPON_TYPE_GUN].x = 2; + weapontexcoord[WEAPON_TYPE_GUN].y = 4; + weapontexcoord[WEAPON_TYPE_GUN].w = 4; + weapontexcoord[WEAPON_TYPE_GUN].h = 2; + weaponprojtexcoord[WEAPON_TYPE_GUN].x = 6; + weaponprojtexcoord[WEAPON_TYPE_GUN].y = 4; + weaponprojtexcoord[WEAPON_TYPE_GUN].w = 2; + weaponprojtexcoord[WEAPON_TYPE_GUN].h = 2; + + nummuzzletex[WEAPON_TYPE_GUN] = 3; + muzzletexcoord[WEAPON_TYPE_GUN][0].x = 8; + muzzletexcoord[WEAPON_TYPE_GUN][0].y = 4; + muzzletexcoord[WEAPON_TYPE_GUN][0].w = 3; + muzzletexcoord[WEAPON_TYPE_GUN][0].h = 2; + muzzletexcoord[WEAPON_TYPE_GUN][1].x = 12; + muzzletexcoord[WEAPON_TYPE_GUN][1].y = 4; + muzzletexcoord[WEAPON_TYPE_GUN][1].w = 3; + muzzletexcoord[WEAPON_TYPE_GUN][1].h = 2; + muzzletexcoord[WEAPON_TYPE_GUN][2].x = 16; + muzzletexcoord[WEAPON_TYPE_GUN][2].y = 4; + muzzletexcoord[WEAPON_TYPE_GUN][2].w = 3; + muzzletexcoord[WEAPON_TYPE_GUN][2].h = 2; + + muzzleparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_GUN].sizey = 40.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_GUN].offsetx = 50.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_GUN].offsety = 6.0f * sizemodifier; + + sizemodifier = 1.3f; + weaponrenderparams[WEAPON_TYPE_ROCKET].sizex = 70.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_ROCKET].sizey = 20.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_ROCKET].offsetx = 24.0f; + weaponrenderparams[WEAPON_TYPE_ROCKET].offsety = -2.0f; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].x = 0; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].y = 8; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].w = 2; + weapontexcoordcursor[WEAPON_TYPE_ROCKET].h = 2; + weapontexcoord[WEAPON_TYPE_ROCKET].x = 2; + weapontexcoord[WEAPON_TYPE_ROCKET].y = 8; + weapontexcoord[WEAPON_TYPE_ROCKET].w = 7; + weapontexcoord[WEAPON_TYPE_ROCKET].h = 2; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].x = 10; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].y = 8; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].w = 2; + weaponprojtexcoord[WEAPON_TYPE_ROCKET].h = 2; + + /*weaponrenderparams[WEAPON_TYPE_SNIPER].sizex = 60.0f; + weaponrenderparams[WEAPON_TYPE_SNIPER].sizey = 20.0f; + weaponrenderparams[WEAPON_TYPE_SNIPER].offsetx = 16.0f; + weaponrenderparams[WEAPON_TYPE_SNIPER].offsety = 4.0f; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].x = 0; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].y = 6; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].w = 2; + weapontexcoordcursor[WEAPON_TYPE_SNIPER].h = 2; + weapontexcoord[WEAPON_TYPE_SNIPER].x = 3; + weapontexcoord[WEAPON_TYPE_SNIPER].y = 6; + weapontexcoord[WEAPON_TYPE_SNIPER].w = 6; + weapontexcoord[WEAPON_TYPE_SNIPER].h = 2; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].x = 10; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].y = 6; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].w = 1; + weaponprojtexcoord[WEAPON_TYPE_SNIPER].h = 1;*/ + + weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizex = 80.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizey = 20.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsetx = 24.0f; + weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsety = -2.0f; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].x = 0; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].y = 6; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].w = 2; + weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].h = 2; + weapontexcoord[WEAPON_TYPE_SHOTGUN].x = 2; + weapontexcoord[WEAPON_TYPE_SHOTGUN].y = 6; + weapontexcoord[WEAPON_TYPE_SHOTGUN].w = 8; + weapontexcoord[WEAPON_TYPE_SHOTGUN].h = 2; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].x = 10; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].y = 6; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].w = 2; + weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].h = 2; + + nummuzzletex[WEAPON_TYPE_SHOTGUN] = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].x = 12; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].y = 6; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].w = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].h = 2; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].x = 16; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].y = 6; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].w = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].h = 2; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].x = 20; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].y = 6; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].w = 3; + muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].h = 2; + + muzzleparams[WEAPON_TYPE_SHOTGUN].sizex = 60.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_SHOTGUN].sizey = 40.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_SHOTGUN].offsetx = 50.0f * sizemodifier; + muzzleparams[WEAPON_TYPE_SHOTGUN].offsety = 6.0f * sizemodifier; + + + + weaponrenderparams[WEAPON_TYPE_MELEE].sizex = 60.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_MELEE].sizey = 50.0f * sizemodifier; + weaponrenderparams[WEAPON_TYPE_MELEE].offsetx = 20.0f; + weaponrenderparams[WEAPON_TYPE_MELEE].offsety = -4.0f; + weapontexcoordcursor[WEAPON_TYPE_MELEE].x = 0; + weapontexcoordcursor[WEAPON_TYPE_MELEE].y = 0; + weapontexcoordcursor[WEAPON_TYPE_MELEE].w = 2; + weapontexcoordcursor[WEAPON_TYPE_MELEE].h = 2; + weapontexcoord[WEAPON_TYPE_MELEE].x = 2; + weapontexcoord[WEAPON_TYPE_MELEE].y = 1; + weapontexcoord[WEAPON_TYPE_MELEE].w = 4; + weapontexcoord[WEAPON_TYPE_MELEE].h = 3; + weaponprojtexcoord[WEAPON_TYPE_MELEE].x = 0; + weaponprojtexcoord[WEAPON_TYPE_MELEE].y = 0; + weaponprojtexcoord[WEAPON_TYPE_MELEE].w = 0; + weaponprojtexcoord[WEAPON_TYPE_MELEE].h = 0; + + + // MODIFIERS + sizemodifier = 2.0; + modifierrenderparams[MODIFIER_TYPE_NINJA].sizex = 60.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_NINJA].sizey = 20.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx = 20.0f; + modifierrenderparams[MODIFIER_TYPE_NINJA].offsety = 4.0f; + modifiertexcoord[MODIFIER_TYPE_NINJA].x = 2; + modifiertexcoord[MODIFIER_TYPE_NINJA].y = 10; + modifiertexcoord[MODIFIER_TYPE_NINJA].w = 7; + modifiertexcoord[MODIFIER_TYPE_NINJA].h = 2; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x = 0; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y = 10; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w = 2; + modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h = 2; + + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizex = 60.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizey = 20.0f * sizemodifier; + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsetx = 16.0f; + modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsety = 4.0f; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].x = 0; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].y = 0; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].w = 0; + modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].h = 0; + + stars[0].x = 0; + stars[0].y = 0; + stars[0].w = 2; + stars[0].h = 2; + + stars[1].x = 0; + stars[1].y = 2; + stars[1].w = 2; + stars[1].h = 2; + + particlecolors[0].x = 0.7f; + particlecolors[0].y = 0.7f; + particlecolors[0].z = 0.7f; + particlecolors[0].w = 1.0f; + particlestexcoord[0].x = 2; + particlestexcoord[0].y = 0; + particlestexcoord[0].w = 2; + particlestexcoord[0].h = 2; + particlecolors[1].x = 1.0f; + particlecolors[1].y = 1.0f; + particlecolors[1].z = 1.0f; + particlecolors[1].w = 1.0f; + particlestexcoord[1].x = 4; + particlestexcoord[1].y = 0; + particlestexcoord[1].w = 2; + particlestexcoord[1].h = 2; + particlecolors[2].x = 0.8f; + particlecolors[2].y = 0.8f; + particlecolors[2].z = 0.8f; + particlecolors[2].w = 1.0f; + particlestexcoord[2].x = 6; + particlestexcoord[2].y = 0; + particlestexcoord[2].w = 2; + particlestexcoord[2].h = 2; + particlecolors[3].x = 0.988f; + particlecolors[3].y = 1.0f; + particlecolors[3].z = 0.16f; + particlecolors[3].w = 1.0f; + particlestexcoord[3].x = 8; + particlestexcoord[3].y = 0; + particlestexcoord[3].w = 2; + particlestexcoord[3].h = 2; + particlecolors[4].x = 1.0f; + particlecolors[4].y = 1.0f; + particlecolors[4].z = 1.0f; + particlecolors[4].w = 1.0f; + particlestexcoord[4].x = 10; + particlestexcoord[4].y = 0; + particlestexcoord[4].w = 2; + particlestexcoord[4].h = 2; + particlecolors[5].x = 0.6f; + particlecolors[5].y = 0.6f; + particlecolors[5].z = 0.6f; + particlecolors[5].w = 1.0f; + particlestexcoord[5].x = 2; + particlestexcoord[5].y = 2; + particlestexcoord[5].w = 2; + particlestexcoord[5].h = 2; + particlecolors[6].x = 1.0f; + particlecolors[6].y = 1.0f; + particlecolors[6].z = 1.0f; + particlecolors[6].w = 1.0f; + particlestexcoord[6].x = 4; + particlestexcoord[6].y = 2; + particlestexcoord[6].w = 2; + particlestexcoord[6].h = 2; + particlecolors[5].x = 0.9f; + particlecolors[5].y = 0.9f; + particlecolors[5].z = 0.9f; + particlecolors[5].w = 1.0f; + particlestexcoord[7].x = 6; + particlestexcoord[7].y = 2; + particlestexcoord[7].w = 2; + particlestexcoord[7].h = 2; + particlecolors[8].x = 1.0f; + particlecolors[8].y = 1.0f; + particlecolors[8].z = 1.0f; + particlecolors[8].w = 1.0f; + particlestexcoord[8].x = 8; + particlestexcoord[8].y = 2; + particlestexcoord[8].w = 2; + particlestexcoord[8].h = 2; + lifemodifier[0] = 0.5f; + lifemodifier[1] = 0.5f; + lifemodifier[2] = 0.5f; + lifemodifier[3] = 0.7f; + lifemodifier[4] = 0.7f; + lifemodifier[5] = 1.0f; + lifemodifier[6] = 1.0f; + lifemodifier[7] = 1.5f; + lifemodifier[8] = 0.4f; + + chaintexcoord.x = 2; + chaintexcoord.y = 0; + chaintexcoord.w = 1; + chaintexcoord.h = 1; + + chainheadtexcoord.x = 3; + chainheadtexcoord.y = 0; + chainheadtexcoord.w = 2; + chainheadtexcoord.h = 1; + + + // anims + anim::setup_hammer(hammeranim); + anim::setup_ninja(ninjaanim); + + for (int i = 0; i < NUMHADOKENS; i++) + { + hadoken[i].x = 1; + hadoken[i].y = 12; + hadoken[i].w = 7; + hadoken[i].h = 4; + hadokenparams[i].sizex = 0.0f; + hadokenparams[i].sizey = 0.0f; + hadokenparams[i].offsetx = 0.0f; + hadokenparams[i].offsety = 0.0f;//-hadokenparams[0].sizey * 0.15f; + } + + // hadoken + hadoken[0].x = 1; + hadoken[0].y = 12; + hadoken[0].w = 7; + hadoken[0].h = 4; + hadokenparams[0].sizex = 70.0f * 2.5f; + hadokenparams[0].sizey = 40.0f * 2.5f; + hadokenparams[0].offsetx = -60.0f; + hadokenparams[0].offsety = 0;//-hadokenparams[0].sizey * 0.15f; + + hadoken[2].x = 8; + hadoken[2].y = 12; + hadoken[2].w = 8; + hadoken[2].h = 4; + hadokenparams[2].sizex = 80.0f * 2.5f; + hadokenparams[2].sizey = 40.0f * 2.5f; + hadokenparams[2].offsetx = -60.0f; + hadokenparams[2].offsety = 0;//-hadokenparams[1].sizey * 0.5f; + + hadoken[4].x = 17; + hadoken[4].y = 12; + hadoken[4].w = 7; + hadoken[4].h = 4; + hadokenparams[4].sizex = 70.0f * 2.5f; + hadokenparams[4].sizey = 40.0f * 2.5f; + hadokenparams[4].offsetx = -60.0f; + hadokenparams[4].offsety = 0;//-hadokenparams[2].sizey * 0.5f; + + // 0 = outline, 1 = body + body[0].x = 2; + body[0].y = 0; + body[0].w = 2; + body[0].h = 2; + body[1].x = 0; + body[1].y = 0; + body[1].w = 2; + body[1].h = 2; + + feet[0].x = 4; + feet[0].y = 1; + feet[0].w = 1; + feet[0].h = 0.5; + feet[1].x = 4; + feet[1].y = 1.52; + feet[1].w = 1; + feet[1].h = 0.48; + + leye.x = 5; + leye.y = 1; + leye.w = 0.5; + leye.h = 0.5; + reye.x = 5; + reye.y = 1.5; + reye.w = 0.5; + reye.h = 0.5; +} + +void modc_entergame() +{ + col_init(32); + img_init(); + tilemap_init(); +} + +void modc_shutdown() +{ +} + +void modc_newsnapshot() +{ + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == EVENT_HEALTHMOD) + { + ev_healthmod *ev = (ev_healthmod *)data; + healthmods.create(vec2(ev->x, ev->y), ev->amount); + } + else if(item.type == EVENT_EXPLOSION) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f); + temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 16; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 16; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 128.0f, 0.985f); + } + + for(int i = 0; i < 64; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(frandom()*256.0f); + temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 24.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_SMOKE) + { + ev_explosion *ev = (ev_explosion *)data; + vec2 p(ev->x, ev->y); + + // center explosion + vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f); + temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f); + v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f); + temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f); + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f); + } + + for(int i = 0; i < 8; i++) + { + vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f); + temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f); + } + } + else if(item.type == EVENT_SOUND) + { + ev_sound *ev = (ev_sound *)data; + vec2 p(ev->x, ev->y); + int sound = (ev->sound & SOUND_MASK); + bool bstartloop = (ev->sound & SOUND_LOOPFLAG_STARTLOOP) != 0; + bool bstoploop = (ev->sound & SOUND_LOOPFLAG_STOPLOOP) != 0; + float vol, pan; + sound_vol_pan(p, &vol, &pan); + + switch(sound) + { + + // FIRE! + case SOUND_FIRE_GUN: + sound_gun_fire.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_SHOTGUN: + sound_shotty_fire.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_ROCKET: + sound_flump_launch.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_MELEE: + sound_hammer_swing.play_random(volume_gun*vol, pan); + break; + case SOUND_FIRE_NINJA: + sound_ninja_attack.play_random(volume_gun*vol, pan); + break; + + // IMPACT + case SOUND_IMPACT_PROJECTILE_GUN: + break; + case SOUND_IMPACT_PROJECTILE_SHOTGUN: + break; + case SOUND_IMPACT_PROJECTILE_ROCKET: + sound_flump_explode.play_random(volume_hit*vol, pan); + break; + + // PLAYER + case SOUND_PLAYER_JUMP: + sound_body_jump.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_HURT_SHORT: + sound_pain_short.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_HURT_LONG: + sound_pain_long.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_SPAWN: + sound_spawn.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_CHAIN_LOOP: + sound_hook_loop.play_random(volume_gun*vol, pan); + break; + case SOUND_PLAYER_CHAIN_IMPACT: + sound_hook_attach.play_random(volume_gun*vol, pan); + break; + case SOUND_PLAYER_IMPACT: + sound_body_land.play_random(volume_hit*vol, pan); + break; + case SOUND_PLAYER_IMPACT_NINJA: + sound_ninja_hit.play_random(volume_hit*vol, pan); + break; + case SOUND_PLAYER_DIE: + sound_body_splat.play_random(volume_tee*vol, pan); + break; + case SOUND_PLAYER_SWITCHWEAPON: + sound_weapon_switch.play_random(volume_gun*vol, pan); + break; + case SOUND_PLAYER_EQUIP: + break; + case SOUND_PLAYER_LAND: + sound_body_land.play_random(volume_tee*vol, pan); + break; + } + } + } +} + +static void render_projectile(obj_projectile *prev, obj_projectile *current) +{ + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + cell_select_ex(numcellsx,numcellsy,weaponprojtexcoord[current->type].x, weaponprojtexcoord[current->type].y, weaponprojtexcoord[current->type].w, weaponprojtexcoord[current->type].h); + vec2 vel(current->vx, current->vy); + + // TODO: interpolare angle aswell + if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0); + + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick()); + gfx_quads_draw(pos.x, pos.y,32,32); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +static void render_powerup(obj_powerup *prev, obj_powerup *current) +{ + //dbg_msg("client", "rendering powerup at %d,%d", current->x, current->y); + + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + float angle = 0.0f; + float sizex = 64.0f; + float sizey = 64.0f; + if (current->type == POWERUP_TYPE_WEAPON) + { + angle = -0.25f * pi * 2.0f; + cell_select_ex(numcellsx,numcellsy,weapontexcoord[current->subtype].x, weapontexcoord[current->subtype].y, weapontexcoord[current->subtype].w, weapontexcoord[current->subtype].h); + sizex = weaponrenderparams[current->subtype].sizex; + sizey = weaponrenderparams[current->subtype].sizey; + } + else + cell_select_ex(numcellsx,numcellsy,poweruptexcoord[current->type].x, poweruptexcoord[current->type].y, poweruptexcoord[current->type].w, poweruptexcoord[current->type].h); + vec2 vel(current->vx, current->vy); + + gfx_quads_setrotation(angle); + // TODO: interpolare angle aswell + /*if(length(vel) > 0.00001f) + gfx_quads_setrotation(get_angle(vel)); + else + gfx_quads_setrotation(0);*/ + + vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick()); + float offset = pos.y/32.0f + pos.x/32.0f; + gfx_quads_draw(pos.x+cosf(client_localtime()*2.0f+offset)*2.5f, pos.y+sinf(client_localtime()*2.0f+offset)*2.5f,sizex * 0.65f,sizey * 0.65f); + gfx_quads_setrotation(0); + gfx_quads_end(); +} + +float getmeleeangle(vec2 direction, obj_player* prev, obj_player* player) +{ + vec2 meleedir(0.53, -0.84); + meleedir = normalize(meleedir); + vec2 meleedirattack(0.95, -0.3); + meleedirattack = normalize(meleedirattack); + + if(direction.x < 0) + { + meleedir.x = -meleedir.x; + meleedirattack.x = -meleedirattack.x; + } + + // 0 -> visualtimeattack go to end pose, (len - visualime) -> go back to normal pose + + float angle = get_angle(meleedir); + if (prev->attackticks) + { + float angleattack = get_angle(meleedirattack); + int phase1tick = (player->attacklen - player->attackticks); + if (phase1tick < player->visualtimeattack) + { + float intratick = snap_intratick(); + float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); + angle = LERP(angle, angleattack, min(1.0f,max(0.0f,t))); + } + else + { + // go back to normal pose + int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks); + float intratick = snap_intratick(); + float t = ((((float)phase2tick) + intratick)/(float)player->visualtimeattack); + angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t))); + } + } + /*if (prev->attackticks && !player->attackticks) + { + // blend back to normal + float angleattack = get_angle(meleedirattack); + angle = LERP(angleattack, angle, min(1.0f,max(0.0f,snap_intratick()))); + } + else if (player->attackticks) + { + float angleattack = get_angle(meleedirattack); + float intratick = snap_intratick(); + float t = ((((float)player->attackticks) - intratick)/(float)player->attacklen); + angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t))); + }*/ + + return angle; +} + +float gethammereangle(vec2 direction, obj_player* prev, obj_player* player) +{ + float t = 0.0f; + if (prev->attackticks) + t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen); + + vec2 pos; + float angle = 0.0f; + hammeranim.evalanim(t,pos,angle); + if(direction.x < 0) + angle = pi -angle;// + ; + //dbg_msg("anim", "Time: %f", t); + return angle; +} + +float getninjaangle(vec2 direction, obj_player* prev, obj_player* player) +{ + float t = 0.0f; + if (prev->attackticks) + t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen); + + vec2 pos; + float angle = 0.0f; + ninjaanim.evalanim(t,pos,angle); + if(direction.x < 0) + angle = pi -angle;// + ; + //dbg_msg("anim", "Time: %f", t); + return angle; +} + + +float getrecoil(obj_player* prev, obj_player* player) +{ + // attack = -10 + float recoil = 0.0f; + if (prev->attackticks) + { + float attackrecoil = recoils[player->weapon]; + int phase1tick = (player->attacklen - player->attackticks); + if (phase1tick < player->visualtimeattack) + { + float intratick = snap_intratick(); + float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); + recoil = LERP(0, attackrecoil, min(1.0f,max(0.0f,t))); + } + else + { + // go back to normal pose + int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks); + float intratick = snap_intratick(); + float t = ((((float)phase2tick) + intratick)/(float)(player->attacklen - player->visualtimeattack)); + recoil = LERP(attackrecoil, 0.0f, min(1.0f,max(0.0f,t))); + } + } + return recoil; +} + +static void render_player(obj_player *prev, obj_player *player) +{ + vec2 direction = get_direction(player->angle); + float angle = player->angle/256.0f; + vec2 position = mix(vec2(prev->x, prev->y), vec2(player->x, player->y), snap_intratick()); + + // draw hook + if(player->hook_active) + { + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + //gfx_quads_begin(); + + vec2 pos = position; + + vec2 hook_pos = mix(vec2(prev->hook_x, prev->hook_y), vec2(player->hook_x, player->hook_y), snap_intratick()); + + float d = distance(pos, hook_pos); + vec2 dir = normalize(pos-hook_pos); + + gfx_quads_setrotation(get_angle(dir)+pi); + + // render head + cell_select_ex(numcellsx,numcellsy, chainheadtexcoord.x,chainheadtexcoord.y, chainheadtexcoord.w, chainheadtexcoord.h); + gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16); + + // render chain + cell_select_ex(numcellsx,numcellsy, chaintexcoord.x, chaintexcoord.y, chaintexcoord.w, chaintexcoord.h); + for(float f = 24; f < d; f += 24) + { + vec2 p = hook_pos + dir*f; + gfx_quads_draw(p.x, p.y,24,16); + } + + gfx_quads_setrotation(0); + gfx_quads_end(); + } + + // draw gun + { + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + gfx_quads_setrotation(angle); + + if (player->modifier & (1 << MODIFIER_TYPE_NINJA)) + { + float playerangle = angle; + // render NINJA!!! (0.53, 0.84) when idle to -> (0.95, 0.3) at the end of attack + if(direction.x < 0) + cell_select_ex_flip_y(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h); + else + cell_select_ex(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h); + + angle = getninjaangle(direction, prev, player);//getmeleeangle(direction, prev, player); + vec2 ninjadir = get_direction(angle * 256.0f); + gfx_quads_setrotation(angle); + vec2 p = position + vec2(0,modifierrenderparams[MODIFIER_TYPE_NINJA].offsety)+ ninjadir * modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx; + // if attack is active hold it differently and draw speedlines behind us? + gfx_quads_draw(p.x,p.y/*+bob*/,modifierrenderparams[MODIFIER_TYPE_NINJA].sizex, modifierrenderparams[MODIFIER_TYPE_NINJA].sizey); + + if ((player->attacklen - player->attackticks) <= (SERVER_TICK_SPEED / 5)) + { + gfx_quads_setrotation(playerangle); + int ihadoken = rand() % NUMHADOKENS; + cell_select_ex(numcellsx, numcellsy, hadoken[ihadoken].x, hadoken[ihadoken].y, hadoken[ihadoken].w, hadoken[ihadoken].h); + vec2 p = position + vec2(0,hadokenparams[ihadoken].offsety)+ direction * hadokenparams[ihadoken].offsetx; + gfx_quads_draw(p.x,p.y/*+bob*/,hadokenparams[ihadoken].sizex, hadokenparams[ihadoken].sizey); + } + } + else + { + // normal weapons + if(direction.x < 0) + cell_select_ex_flip_y(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h); + else + cell_select_ex(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h); + + vec2 dir = direction; + float recoil = 0.0f; + if (player->weapon == WEAPON_TYPE_MELEE) + { + // if attack is under way, bash stuffs + //angle = getmeleeangle(direction, prev, player); + angle = gethammereangle(direction, prev, player); + gfx_quads_setrotation(angle); + dir = get_direction(angle * 256.0f); + } + else + { + recoil = getrecoil(prev, player); + } + + vec2 p = position + vec2(0,weaponrenderparams[player->weapon].offsety) + dir * weaponrenderparams[player->weapon].offsetx - dir * recoil; + gfx_quads_draw(p.x,p.y/*+bob*/,weaponrenderparams[player->weapon].sizex, weaponrenderparams[player->weapon].sizey); + // draw muzzleflare + if (player->weapon == WEAPON_TYPE_GUN || player->weapon == WEAPON_TYPE_SHOTGUN) + { + // check if we're firing stuff + if (true)///prev->attackticks) + { + float alpha = 0.0f; + int phase1tick = (player->attacklen - player->attackticks); + if (phase1tick < (player->visualtimeattack + 3)) + { + float intratick = snap_intratick(); + float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack); + alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t))); + } + + if (alpha > 0.0f) + { + float offsety = -muzzleparams[player->weapon].offsety; + int itex = rand() % nummuzzletex[player->weapon]; + if(direction.x < 0) + { + offsety = -offsety; + cell_select_ex_flip_y(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h); + } + else + cell_select_ex(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h); + + gfx_quads_setcolor(1.0f,1.0f,1.0f,alpha); + vec2 diry(-dir.y,dir.x); + p += dir * muzzleparams[player->weapon].offsetx + diry * offsety; + gfx_quads_draw(p.x,p.y/*+bob*/,muzzleparams[player->weapon].sizex, muzzleparams[player->weapon].sizey); + } + } + } + } + /*else + { + // minigun + if(direction.x < 0) + cell_select_flip_y(4,4,8,2); + else + cell_select(4,4,8,2); + vec2 p = position + vec2(0,3); + gfx_quads_draw(p.x,p.y,8*8,8*2); + }*/ + + gfx_quads_setrotation(0); + gfx_quads_end(); + } + + + gfx_texture_set(texture_char_default); + gfx_quads_begin(); + + float bob = 0; + + // draw foots + const float cyclelength = 128.0f; + const float steplength = 26; + const float lift = 4.0f; + bool stationary = player->vx < 1 && player->vx > -1; + bool inair = col_check_point(player->x, player->y+16) == 0; + + for(int p = 0; p < 2; p++) + { + // first pass we draw the outline + // second pass we draw the filling + + //int v_offset = p?0:5; + int outline = p;// ? 1 : 0; + float offsety = charids[player->clientid % 16] * 2.0f; + + for(int f = 0; f < 2; f++) + { + float basesize = 10.0f; + if(f == 1) + { + // draw body + float t = fmod(position.x, cyclelength/2)/(cyclelength/2); + bob = -sinf(pow(t,2)*pi) * 3; + cell_select_ex(charnumcellsx,charnumcellsy, body[outline].x,body[outline].y + offsety,body[outline].w,body[outline].h); + //cell_select_ex(16,16, 0,0+v_offset,4,4); + //const float size = 64.0f; + if(stationary || inair) + bob = 0; + gfx_quads_draw(position.x, position.y-5+bob, 4*basesize, 4*basesize); + + // draw eyes + if(p == 1) + { + //cell_select_ex(16,16, 8,3,1,1); + vec2 md = get_direction(player->angle); + float mouse_dir_x = md.x; + float mouse_dir_y = md.y; + + // normal + cell_select_ex(charnumcellsx,charnumcellsy, leye.x,leye.y + offsety,leye.w,leye.h); + gfx_quads_draw(position.x-4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize); + cell_select_ex(charnumcellsx,charnumcellsy, reye.x,reye.y + offsety,reye.w,reye.h); + gfx_quads_draw(position.x+4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize); + } + } + + // draw feet + //cell_select_ex(16,16, 5,2+v_offset, 2,2); + cell_select_ex(charnumcellsx,charnumcellsy, feet[outline].x,feet[outline].y + offsety, feet[outline].w,feet[outline].h); + float w = basesize*2.5f; + float h = basesize*1.425f; + if(inair) + { + float r = 0.0f; + if(player->vy < 0.0f) + r = player->vy/3.0f; + else + r = player->vy/15.0f; + + // clamp the rotation + if(r > 0.5f) r = 0.5f; + if(r < -0.5f) r = -0.5f; + + if(player->vx > 0.0f) + r *= -1.0f; + gfx_quads_setrotation(r); + gfx_quads_drawTL(position.x-4+f*7-w/2, position.y+16 - h, w, h); + gfx_quads_setrotation(0); + } + else if(stationary) + { + // stationary + gfx_quads_drawTL(position.x-7+f*14-w/2, position.y+16 - h, w, h); + } + else + { + /* + The walk cycle, 2 parts + + 111 + 1 1 + 2 1 + 2 1 + 2222221 + GROUND GROUND GROUND + */ + + // moving + float tx = position.x+f*(cyclelength/2); + float t = fmod(tx, cyclelength) / cyclelength; + if(player->vx < 0) + t = 1.0f-t; + + float y; + float x = 0; + float r = 0; + float r_back = 1.5f; + + if(t < 0.5f) + { + // stomp down foot (part 1) + float st = t*2; + y = 1.0f-pow(st, 0.5f) + sinf(pow(st,2)*pi)*0.5f; + x = -steplength/2 + st*steplength; + r = r_back*(1-st) + sinf(pow(st,1.5f)*pi*2); + } + else + { + // lift foot up again (part 2) + float st = (t-0.5f)*2; + y = pow(st, 5.0f); + x = steplength/2 - st*steplength; + r = y*r_back; + } + + + if(player->vx > 0) + { + gfx_quads_setrotation(r); + gfx_quads_drawTL(position.x+x-w/2, position.y+16-y*lift - h, w, h); + } + else + { + gfx_quads_setrotation(-r); + gfx_quads_drawTL(position.x-x-w/2, position.y+16-y*lift - h, w, h); + } + gfx_quads_setrotation(0); + } + + } + } + + gfx_quads_end(); + + +} + +static player_input oldinput; +static bool bfirst = true; +void modc_render() +{ + if (bfirst) + { + bfirst = false; + oldinput.activeweapon = 0; + oldinput.angle = 0; + oldinput.blink = 0; + oldinput.fire = 0; + oldinput.hook = 0; + oldinput.jump = 0; + oldinput.left = 0; + oldinput.right = 0; + } + // fetch new input + { + int x, y; + inp_mouse_relative(&x, &y); + mouse_pos += vec2(x, y); + float l = length(mouse_pos); + if(l > 600.0f) + mouse_pos = normalize(mouse_pos)*600.0f; + } + + // snap input + { + player_input input; + input.left = inp_key_pressed('A'); + input.right = inp_key_pressed('D'); + float a = atan((float)mouse_pos.y/(float)mouse_pos.x); + if(mouse_pos.x < 0) + a = a+pi; + input.angle = (int)(a*256.0f); + input.jump = inp_key_pressed(baselib::keys::space) || inp_key_pressed('W'); + + input.fire = inp_mouse_button_pressed(0);// | (oldinput.fire << 16); + //oldinput.fire = input.fire & 0x0000ffff; + + input.hook = inp_mouse_button_pressed(1) || inp_key_pressed(baselib::keys::lctrl); // be nice to mac users O.o + input.blink = inp_key_pressed('S'); + + // Weapon switching + input.activeweapon = inp_key_pressed('1') ? 0x80000000 : 0; + if (!input.activeweapon) + input.activeweapon = inp_key_pressed('2') ? 0x80000000 | 1 : 0; + if (!input.activeweapon) + input.activeweapon = inp_key_pressed('3') ? 0x80000000 | 2 : 0; + if (!input.activeweapon) + input.activeweapon = inp_key_pressed('4') ? 0x80000000 | 3 : 0; + /*if (!input.activeweapon) + input.activeweapon = inp_key_pressed('5') ? 0x80000000 | 4 : 0;*/ + + snap_input(&input, sizeof(input)); + } + + // setup world view + { + // 1. fetch local player + // 2. set him to the center + + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER) + { + obj_player *player = (obj_player *)data; + if(player->local) + { + local_player = player; + local_player_pos = vec2(player->x, player->y); + + void *p = snap_find_item(SNAP_PREV, item.type, item.id); + if(p) + local_player_pos = mix(vec2(((obj_player *)p)->x, ((obj_player *)p)->y), local_player_pos, snap_intratick()); + break; + } + } + } + } + + // pseudo format + float zoom = inp_key_pressed('T') ? 1.0 : 3.0f; + + float width = 400*zoom; + float height = 300*zoom; + float screen_x = 0; + float screen_y = 0; + + // center at char but can be moved when mouse is far away + float offx = 0, offy = 0; + int deadzone = 300; + if(mouse_pos.x > deadzone) offx = mouse_pos.x-deadzone; + if(mouse_pos.x <-deadzone) offx = mouse_pos.x+deadzone; + if(mouse_pos.y > deadzone) offy = mouse_pos.y-deadzone; + if(mouse_pos.y <-deadzone) offy = mouse_pos.y+deadzone; + offx = offx*2/3; + offy = offy*2/3; + + screen_x = local_player_pos.x+offx; + screen_y = local_player_pos.y+offy; + + gfx_mapscreen(screen_x-width/2, screen_y-height/2, screen_x+width/2, screen_y+height/2); + + // draw background + gfx_clear(0.65f,0.78f,0.9f); + + { + + vec2 pos(local_player_pos.x*0.5f, local_player_pos.y*0.5f); + + gfx_texture_set(-1); + gfx_blend_additive(); + gfx_quads_begin(); + const int rays = 10; + gfx_quads_setcolor(1.0f,1.0f,1.0f,0.025f); + for(int r = 0; r < rays; r++) + { + float a = r/(float)rays + client_localtime()*0.05f; + float size = (1.0f/(float)rays)*0.25f; + vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f)); + vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f)); + + //gfx_quads_draw_freeform(0,0, -100,0, -100,-100, 0,-100); + + gfx_quads_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f); + gfx_quads_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f); + gfx_quads_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f); + gfx_quads_setcolorvertex(3, 1.0f,1.0f,1.0f,0.0f); + const float range = 1000.0f; + gfx_quads_draw_freeform( + pos.x+dir0.x, pos.y+dir0.y, + pos.x+dir1.x, pos.y+dir1.y, + pos.x+dir0.x*range, pos.y+dir0.y*range, + pos.x+dir1.x*range, pos.y+dir1.y*range); + } + gfx_quads_end(); + gfx_blend_normal(); + + gfx_texture_set(texture_sun); + gfx_quads_begin(); + gfx_quads_draw(pos.x, pos.y, 256, 256); + gfx_quads_end(); + } + + // render map + tilemap_render(32.0f, 0); +#ifdef _DEBUG + float speed = 0.0f; +#endif + // render items + int num = snap_num_items(SNAP_CURRENT); + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER) + { + void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + { + render_player((obj_player *)prev, (obj_player *)data); +/*#ifdef _DEBUG + { + obj_player *p = (obj_player *)prev; + obj_player *c = (obj_player *)data; + vec2 positionold = vec2(p->x, p->y); + vec2 poscur = vec2(c->x, c->y); + speed = distance(positionold,poscur); + } +#endif*/ + } + } + else if(item.type == OBJTYPE_PROJECTILE) + { + void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_projectile((obj_projectile *)prev, (obj_projectile *)data); + } + else if(item.type == OBJTYPE_POWERUP) + { + void *prev = snap_find_item(SNAP_PREV, item.type, item.id); + if(prev) + render_powerup((obj_powerup*)prev, (obj_powerup *)data); + } + } + + // render particles + temp_system.update(client_frametime()); + temp_system.render(); + + tilemap_render(32.0f, 1); + + // render health mods + healthmods.render(); + + // render cursor + // FIXME CURSOR!!! + + if(local_player) + { + gfx_texture_set(texture_weapon); + gfx_quads_begin(); + if (local_player->modifier & (1 << MODIFIER_TYPE_NINJA)) + cell_select_ex(numcellsx,numcellsy, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h); + else + cell_select_ex(numcellsx,numcellsy, weapontexcoordcursor[local_player->weapon].x, weapontexcoordcursor[local_player->weapon].y, weapontexcoordcursor[local_player->weapon].w, weapontexcoordcursor[local_player->weapon].h); + float cursorsize = 64; + gfx_quads_draw(local_player_pos.x+mouse_pos.x, local_player_pos.y+mouse_pos.y,cursorsize,cursorsize); + + + // render ammo count + // render gui stuff + gfx_quads_end(); + gfx_quads_begin(); + gfx_mapscreen(0,0,400,300); + cell_select_ex(numcellsx,numcellsy, weaponprojtexcoord[local_player->weapon].x, weaponprojtexcoord[local_player->weapon].y, weaponprojtexcoord[local_player->weapon].w, weaponprojtexcoord[local_player->weapon].h); + for (int i = 0; i < local_player->ammocount; i++) + { + gfx_quads_drawTL(10+i*12,34,10,10); + } + gfx_quads_end(); + + gfx_texture_set(texture_game); + gfx_quads_begin(); + int h = 0; + cell_select_ex(32,16, 0,0, 4,4); + for(; h < local_player->health; h++) + gfx_quads_drawTL(10+h*12,10,10,10); + + cell_select_ex(32,16, 5,0, 4,4); + for(; h < 10; h++) + gfx_quads_drawTL(10+h*12,10,10,10); + + h = 0; + cell_select_ex(32,16, 0,5, 4,4); + for(; h < local_player->armor; h++) + gfx_quads_drawTL(10+h*12,22,10,10); + + cell_select_ex(32,16, 5,5, 4,4); + for(; h < 10; h++) + gfx_quads_drawTL(10+h*12,22,10,10); + gfx_quads_end(); + + // render speed +/*#ifdef _DEBUG + gfx_texture_set(font_texture); + char text[256]; + sprintf(text,"speed: %f",speed); + gfx_quads_text(300,20,10,text); +#endif*/ + } + // render gui stuff + gfx_mapscreen(0,0,400,300); + // render score board + if(inp_key_pressed(baselib::keys::tab)) + { + gfx_texture_set(font_texture); + gfx_quads_text(10, 50, 8, "Score Board"); + + int num = snap_num_items(SNAP_CURRENT); + int row = 1; + for(int i = 0; i < num; i++) + { + snap_item item; + void *data = snap_get_item(SNAP_CURRENT, i, &item); + + if(item.type == OBJTYPE_PLAYER) + { + obj_player *player = (obj_player *)data; + if(player) + { + char buf[128]; + char name[32]; + snap_decode_string(player->name, name, 32); + sprintf(buf, "%4d %s", player->score, name); + gfx_quads_text(10, 50 + 10 * row, 8, buf); + row++; + } + } + } + } +} diff --git a/src/game/game_server.cpp b/src/game/game_server.cpp new file mode 100644 index 00000000..5e93165a --- /dev/null +++ b/src/game/game_server.cpp @@ -0,0 +1,2122 @@ +#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() {} diff --git a/src/game/mapres.h b/src/game/mapres.h new file mode 100644 index 00000000..8d09e99c --- /dev/null +++ b/src/game/mapres.h @@ -0,0 +1,7 @@ +enum +{ + MAPRES_REGISTERED=0x8000, + MAPRES_IMAGE=0x8001, + MAPRES_TILEMAP=0x8002, + MAPRES_COLLISIONMAP=0x8003, +}; diff --git a/src/game/mapres_col.cpp b/src/game/mapres_col.cpp new file mode 100644 index 00000000..0cf71986 --- /dev/null +++ b/src/game/mapres_col.cpp @@ -0,0 +1,44 @@ +#include <baselib/system.h> +#include "../interface.h" +#include "mapres_col.h" +#include "mapres.h" + +/* + Simple collision rutines! +*/ +struct collision +{ + int w, h; + unsigned char *data; +}; + +static collision col; +static int global_dividor; + +int col_init(int dividor) +{ + mapres_collision *c = (mapres_collision*)map_find_item(MAPRES_COLLISIONMAP,0); + if(!c) + { + dbg_msg("mapres_col", "failed!"); + return 0; + } + col.w = c->width; + col.h = c->height; + global_dividor = dividor; + col.data = (unsigned char *)map_get_data(c->data_index); + return col.data ? 1 : 0; +} + +int col_check_point(int x, int y) +{ + int nx = x/global_dividor; + int ny = y/global_dividor; + if(nx < 0 || nx >= col.w || ny >= col.h) + return 1; + + if(y < 0) + return 0; // up == sky == free + + return col.data[ny*col.w+nx]; +} diff --git a/src/game/mapres_col.h b/src/game/mapres_col.h new file mode 100644 index 00000000..2afad439 --- /dev/null +++ b/src/game/mapres_col.h @@ -0,0 +1,10 @@ + +struct mapres_collision +{ + int width; + int height; + int data_index; +}; + +int col_init(int dividor); +int col_check_point(int x, int y); diff --git a/src/game/mapres_image.cpp b/src/game/mapres_image.cpp new file mode 100644 index 00000000..baf7f09b --- /dev/null +++ b/src/game/mapres_image.cpp @@ -0,0 +1,41 @@ +#include <baselib/system.h> +#include "../interface.h" +#include "mapres_image.h" +#include "mapres.h" + +static int map_textures[64] = {0}; +static int count = 0; + +int img_init() +{ + int start, count; + map_get_type(MAPRES_IMAGE, &start, &count); + dbg_msg("mapres_image", "start=%d count=%d", start, count); + for(int i = 0; i < 64; i++) + { + if(map_textures[i]) + { + gfx_unload_texture(map_textures[i]); + map_textures[i] = 0; + } + } + + for(int i = 0; i < count; i++) + { + mapres_image *img = (mapres_image *)map_get_item(start+i, 0, 0); + void *data = map_get_data(img->image_data); + map_textures[i] = gfx_load_texture_raw(img->width, img->height, data); + } + + return count; +} + +int img_num() +{ + return count; +} + +int img_get(int index) +{ + return map_textures[index]; +} diff --git a/src/game/mapres_image.h b/src/game/mapres_image.h new file mode 100644 index 00000000..eab1559a --- /dev/null +++ b/src/game/mapres_image.h @@ -0,0 +1,18 @@ + +// loads images from the map to textures +int img_init(); + +// returns the number of images in the map +int img_num(); + +// fetches the texture id for the image +int img_get(int index); + + +class mapres_image +{ +public: + int width; + int height; + int image_data; +}; diff --git a/src/game/mapres_tilemap.cpp b/src/game/mapres_tilemap.cpp new file mode 100644 index 00000000..0868d2e4 --- /dev/null +++ b/src/game/mapres_tilemap.cpp @@ -0,0 +1,54 @@ +#include "../interface.h" +#include "mapres_tilemap.h" +#include "mapres_image.h" +#include "mapres.h" + +int tilemap_init() +{ + return 0; +} + +void tilemap_render(float scale, int fg) +{ + if(!map_is_loaded()) + return; + + // fetch indecies + int start, num; + map_get_type(MAPRES_TILEMAP, &start, &num); + + // render tilemaps + int passed_main = 0; + for(int t = 0; t < num; t++) + { + mapres_tilemap *tmap = (mapres_tilemap *)map_get_item(start+t,0,0); + unsigned char *data = (unsigned char *)map_get_data(tmap->data); + + if(tmap->main) + passed_main = 1; + + if((fg && passed_main) || (!fg && !passed_main)) + { + gfx_texture_set(img_get(tmap->image)); + gfx_quads_begin(); + + int c = 0; + float frac = (1.0f/1024.0f); //2.0f; + for(int y = 0; y < tmap->height; y++) + for(int x = 0; x < tmap->width; x++, c++) + { + unsigned char d = data[c*2]; + if(d) + { + gfx_quads_setsubset( + (d%16)/16.0f+frac, + (d/16)/16.0f+frac, + (d%16)/16.0f+1.0f/16.0f-frac, + (d/16)/16.0f+1.0f/16.0f-frac); + gfx_quads_drawTL(x*scale, y*scale, scale, scale); + } + } + gfx_quads_end(); + } + } +} diff --git a/src/game/mapres_tilemap.h b/src/game/mapres_tilemap.h new file mode 100644 index 00000000..6e9d81be --- /dev/null +++ b/src/game/mapres_tilemap.h @@ -0,0 +1,19 @@ + +// dependencies: image + +// +int tilemap_init(); + +// renders the tilemaps +void tilemap_render(float scale, int fg); + +struct mapres_tilemap +{ + int image; + int width; + int height; + int x, y; + int scale; + int data; + int main; +}; |