#ifdef _WIN32 # include #endif #include "EXTERN.h" #include "perl.h" #include "XSUB.h" #include #include #include #include #include #include #include #include #include #include #ifndef _WIN32 # include # include # include # include # include #else typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef signed char int8_t; typedef signed short int16_t; typedef signed int int32_t; #endif #include "glext.h" #define FOW_DARKNESS 32 #define MAP_EXTEND_X 32 #define MAP_EXTEND_Y 512 #define MIN_FONT_HEIGHT 8 * PANGO_SCALE typedef Mix_Chunk *CFClient__MixChunk; typedef Mix_Music *CFClient__MixMusic; static PangoContext *context; static PangoFontMap *fontmap; typedef struct cf_layout { PangoLayout *pl; int base_height; } *CFClient__Layout; static void substitute_func (FcPattern *pattern, gpointer data) { FcPatternAddBool (pattern, FC_HINTING , 1); FcPatternAddBool (pattern, FC_AUTOHINT, 0); } static void layout_update (CFClient__Layout self) { /* use a random scale factor to account for unknown descenders, 0.8 works * reasonably well with bitstream vera */ PangoFontDescription *font = pango_layout_get_font_description (self->pl); int height = self->base_height * (PANGO_SCALE * 8 / 10); if (height < MIN_FONT_HEIGHT) height = MIN_FONT_HEIGHT; if (pango_font_description_get_size (font) != height) { font = pango_font_description_copy (font); pango_font_description_set_absolute_size (font, height); pango_layout_set_font_description (self->pl, font); } } static void layout_get_pixel_size (CFClient__Layout self, int *w, int *h) { layout_update (self); pango_layout_get_pixel_size (self->pl, w, h); *w = (*w + 3) & ~3; if (!*w) *w = 1; if (!*h) *h = 1; } typedef uint16_t mapface; typedef struct { GLint name; int w, h; float s, t; uint8_t r, g, b, a; } maptex; typedef struct { int16_t darkness; mapface face[3]; } mapcell; typedef struct { int32_t c0, c1; mapcell *col; } maprow; typedef struct map { int x, y, w, h; int ox, oy; /* offset to virtual global coordinate system */ int faces; mapface *face; int texs; maptex *tex; int32_t rows; maprow *row; } *CFClient__Map; static char * prepend (char *ptr, int sze, int inc) { char *p; New (0, p, sze + inc, char); Zero (p, inc, char); Move (ptr, p + inc, sze, char); Safefree (ptr); return p; } static char * append (char *ptr, int sze, int inc) { Renew (ptr, sze + inc, char); Zero (ptr + sze, inc, char); return ptr; } #define Append(type,ptr,sze,inc) (ptr) = (type *)append ((char *)ptr, (sze) * sizeof (type), (inc) * sizeof (type)) #define Prepend(type,ptr,sze,inc) (ptr) = (type *)prepend ((char *)ptr, (sze) * sizeof (type), (inc) * sizeof (type)) static maprow * map_get_row (CFClient__Map self, int y) { if (0 > y) { int extend = - y + MAP_EXTEND_Y; Prepend (maprow, self->row, self->rows, extend); self->rows += extend; self->y += extend; y += extend; } else if (y >= self->rows) { int extend = y - self->rows + MAP_EXTEND_Y; Append (maprow, self->row, self->rows, extend); self->rows += extend; } return self->row + y; } static mapcell * row_get_cell (maprow *row, int x) { if (!row->col) { Newz (0, row->col, MAP_EXTEND_X, mapcell); row->c0 = x - MAP_EXTEND_X / 4; row->c1 = row->c0 + MAP_EXTEND_X; } if (row->c0 > x) { int extend = row->c0 - x + MAP_EXTEND_X; Prepend (mapcell, row->col, row->c1 - row->c0, extend); row->c0 -= extend; } else if (x >= row->c1) { int extend = x - row->c1 + MAP_EXTEND_X; Append (mapcell, row->col, row->c1 - row->c0, extend); row->c1 += extend; } return row->col + (x - row->c0); } static mapcell * map_get_cell (CFClient__Map self, int x, int y) { return row_get_cell (map_get_row (self, y), x); } static void map_clear (CFClient__Map self) { int r; for (r = 0; r < self->rows; r++) Safefree (self->row[r].col); Safefree (self->row); self->x = 0; self->y = 0; self->ox = 0; self->oy = 0; self->row = 0; self->rows = 0; } static void map_blank (CFClient__Map self, int x0, int y0, int w, int h) { int x, y; maprow *row; for (y = y0; y < y0 + h; y++) if (y >= 0) { if (y >= self->rows) break; row = self->row + y; for (x = x0; x < x0 + w; x++) if (x >= row->c0) { if (x >= row->c1) break; row->col[x - row->c0].darkness = -1; } } } static void music_finished () { SDL_UserEvent ev; ev.type = SDL_USEREVENT; ev.code = 0; ev.data1 = 0; ev.data2 = 0; SDL_PushEvent ((SDL_Event *)&ev); } MODULE = CFClient PACKAGE = CFClient PROTOTYPES: ENABLE BOOT: { HV *stash = gv_stashpv ("CFClient", 1); static const struct { const char *name; IV iv; } *civ, const_iv[] = { # define const_iv(name) { # name, (IV)name } const_iv (SDL_ACTIVEEVENT), const_iv (SDL_KEYDOWN), const_iv (SDL_KEYUP), const_iv (SDL_MOUSEMOTION), const_iv (SDL_MOUSEBUTTONDOWN), const_iv (SDL_MOUSEBUTTONUP), const_iv (SDL_JOYAXISMOTION), const_iv (SDL_JOYBALLMOTION), const_iv (SDL_JOYHATMOTION), const_iv (SDL_JOYBUTTONDOWN), const_iv (SDL_JOYBUTTONUP), const_iv (SDL_QUIT), const_iv (SDL_SYSWMEVENT), const_iv (SDL_EVENT_RESERVEDA), const_iv (SDL_EVENT_RESERVEDB), const_iv (SDL_VIDEORESIZE), const_iv (SDL_VIDEOEXPOSE), const_iv (SDL_USEREVENT), const_iv (SDLK_KP0), const_iv (SDLK_KP1), const_iv (SDLK_KP2), const_iv (SDLK_KP3), const_iv (SDLK_KP4), const_iv (SDLK_KP5), const_iv (SDLK_KP6), const_iv (SDLK_KP7), const_iv (SDLK_KP8), const_iv (SDLK_KP9), const_iv (SDLK_KP_PERIOD), const_iv (SDLK_KP_DIVIDE), const_iv (SDLK_KP_MULTIPLY), const_iv (SDLK_KP_MINUS), const_iv (SDLK_KP_PLUS), const_iv (SDLK_KP_ENTER), const_iv (SDLK_KP_EQUALS), const_iv (SDLK_UP), const_iv (SDLK_DOWN), const_iv (SDLK_RIGHT), const_iv (SDLK_LEFT), const_iv (SDLK_INSERT), const_iv (SDLK_HOME), const_iv (SDLK_END), const_iv (SDLK_PAGEUP), const_iv (SDLK_PAGEDOWN), const_iv (SDLK_F1), const_iv (SDLK_F2), const_iv (SDLK_F3), const_iv (SDLK_F4), const_iv (SDLK_F5), const_iv (SDLK_F6), const_iv (SDLK_F7), const_iv (SDLK_F8), const_iv (SDLK_F9), const_iv (SDLK_F10), const_iv (SDLK_F11), const_iv (SDLK_F12), const_iv (SDLK_F13), const_iv (SDLK_F14), const_iv (SDLK_F15), const_iv (SDLK_NUMLOCK), const_iv (SDLK_CAPSLOCK), const_iv (SDLK_SCROLLOCK), const_iv (SDLK_RSHIFT), const_iv (SDLK_LSHIFT), const_iv (SDLK_RCTRL), const_iv (SDLK_LCTRL), const_iv (SDLK_RALT), const_iv (SDLK_LALT), const_iv (SDLK_RMETA), const_iv (SDLK_LMETA), const_iv (SDLK_LSUPER), const_iv (SDLK_RSUPER), const_iv (SDLK_MODE), const_iv (SDLK_COMPOSE), const_iv (SDLK_HELP), const_iv (SDLK_PRINT), const_iv (SDLK_SYSREQ), const_iv (SDLK_BREAK), const_iv (SDLK_MENU), const_iv (SDLK_POWER), const_iv (SDLK_EURO), const_iv (SDLK_UNDO), const_iv (KMOD_NONE), const_iv (KMOD_LSHIFT), const_iv (KMOD_RSHIFT), const_iv (KMOD_LCTRL), const_iv (KMOD_RCTRL), const_iv (KMOD_LALT), const_iv (KMOD_RALT), const_iv (KMOD_LMETA), const_iv (KMOD_RMETA), const_iv (KMOD_NUM), const_iv (KMOD_CAPS), const_iv (KMOD_MODE), const_iv (KMOD_CTRL), const_iv (KMOD_SHIFT), const_iv (KMOD_ALT), const_iv (KMOD_META) # undef const_iv }; for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ-- > const_iv; ) newCONSTSUB (stash, (char *)civ->name, newSViv (civ->iv)); fontmap = pango_ft2_font_map_new (); pango_ft2_font_map_set_default_substitute ((PangoFT2FontMap *)fontmap, substitute_func, 0, 0); context = pango_ft2_font_map_create_context ((PangoFT2FontMap *)fontmap); } int SDL_Init (U32 flags = SDL_INIT_VIDEO | SDL_INIT_AUDIO) void SDL_Quit () void SDL_ListModes () PPCODE: { SDL_Rect **m; SDL_GL_SetAttribute (SDL_GL_RED_SIZE, 5); SDL_GL_SetAttribute (SDL_GL_GREEN_SIZE, 5); SDL_GL_SetAttribute (SDL_GL_BLUE_SIZE, 5); SDL_GL_SetAttribute (SDL_GL_ALPHA_SIZE, 1); SDL_GL_SetAttribute (SDL_GL_ACCUM_RED_SIZE, 0); SDL_GL_SetAttribute (SDL_GL_ACCUM_GREEN_SIZE, 0); SDL_GL_SetAttribute (SDL_GL_ACCUM_BLUE_SIZE, 0); SDL_GL_SetAttribute (SDL_GL_ACCUM_ALPHA_SIZE, 0); SDL_GL_SetAttribute (SDL_GL_DOUBLEBUFFER, 1); SDL_GL_SetAttribute (SDL_GL_BUFFER_SIZE, 15); SDL_GL_SetAttribute (SDL_GL_DEPTH_SIZE, 0); SDL_EnableUNICODE (1); SDL_EnableKeyRepeat (SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL); m = SDL_ListModes (0, SDL_FULLSCREEN | SDL_OPENGL); if (m && m != (SDL_Rect **)-1) while (*m) { AV *av = newAV (); av_push (av, newSViv ((*m)->w)); av_push (av, newSViv ((*m)->h)); XPUSHs (sv_2mortal (newRV_noinc ((SV *)av))); ++m; } } int SDL_SetVideoMode (int w, int h, int fullscreen) CODE: RETVAL = !!SDL_SetVideoMode ( w, h, 0, SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0) ); SDL_WM_SetCaption ("Crossfire+ Client " VERSION, "Crossfire+"); OUTPUT: RETVAL void SDL_GL_SwapBuffers () void SDL_PollEvent () PPCODE: { SDL_Event ev; while (SDL_PollEvent (&ev)) { HV *hv = newHV (); hv_store (hv, "type", 4, newSViv (ev.type), 0); switch (ev.type) { case SDL_KEYDOWN: case SDL_KEYUP: hv_store (hv, "state", 5, newSViv (ev.key.state), 0); hv_store (hv, "sym", 3, newSViv (ev.key.keysym.sym), 0); hv_store (hv, "mod", 3, newSViv (ev.key.keysym.mod), 0); hv_store (hv, "unicode", 7, newSViv (ev.key.keysym.unicode), 0); break; case SDL_ACTIVEEVENT: hv_store (hv, "gain", 4, newSViv (ev.active.gain), 0); hv_store (hv, "state", 5, newSViv (ev.active.state), 0); break; case SDL_MOUSEMOTION: hv_store (hv, "state", 5, newSViv (ev.motion.state), 0); hv_store (hv, "x", 1, newSViv (ev.motion.x), 0); hv_store (hv, "y", 1, newSViv (ev.motion.y), 0); hv_store (hv, "xrel", 4, newSViv (ev.motion.xrel), 0); hv_store (hv, "yrel", 4, newSViv (ev.motion.yrel), 0); break; case SDL_MOUSEBUTTONDOWN: case SDL_MOUSEBUTTONUP: hv_store (hv, "button", 6, newSViv (ev.button.button), 0); hv_store (hv, "state", 5, newSViv (ev.button.state), 0); hv_store (hv, "x", 1, newSViv (ev.button.x), 0); hv_store (hv, "y", 1, newSViv (ev.button.y), 0); } XPUSHs (sv_2mortal (newRV_noinc ((SV *)hv))); } } int Mix_OpenAudio (int frequency = 22050, int format = MIX_DEFAULT_FORMAT, int channels = 1, int chunksize = 512) POSTCALL: Mix_HookMusicFinished (music_finished); void Mix_CloseAudio () int Mix_AllocateChannels (int numchans = -1) void lowdelay (int fd, int val = 1) CODE: #ifndef _WIN32 setsockopt (fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof (val)); #endif char * gl_version () CODE: RETVAL = (char *)glGetString (GL_VERSION); OUTPUT: RETVAL char * gl_extensions () CODE: RETVAL = (char *)glGetString (GL_EXTENSIONS); OUTPUT: RETVAL void add_font (char *file) CODE: FcConfigAppFontAddFile (0, (const FcChar8 *)file); /* no idea wether this is required */ void set_font (char *file) CODE: { int count; FcPattern *pattern = FcFreeTypeQuery ((const FcChar8 *)file, 0, 0, &count); PangoFontDescription *font = pango_fc_font_description_from_pattern (pattern, 0); FcPatternDestroy (pattern); pango_font_description_set_absolute_size (font, MIN_FONT_HEIGHT); pango_context_set_font_description (context, font); } void load_image_inline (SV *image_) ALIAS: load_image_file = 1 PPCODE: { STRLEN image_len; char *image = (char *)SvPVbyte (image_, image_len); SDL_Surface *surface, *surface2; SDL_PixelFormat fmt; SDL_RWops *rw = ix ? SDL_RWFromFile (image, "r") : SDL_RWFromConstMem (image, image_len); if (!rw) croak ("load_image: %s", SDL_GetError ()); surface = IMG_Load_RW (rw, 1); if (!surface) croak ("load_image: %s", SDL_GetError ()); fmt.palette = NULL; fmt.BitsPerPixel = 32; fmt.BytesPerPixel = 4; #if SDL_BYTEORDER == SDL_LIL_ENDIAN fmt.Rmask = 0x000000ff; fmt.Gmask = 0x0000ff00; fmt.Bmask = 0x00ff0000; fmt.Amask = 0xff000000; #else fmt.Rmask = 0xff000000; fmt.Gmask = 0x00ff0000; fmt.Bmask = 0x0000ff00; fmt.Amask = 0x000000ff; #endif fmt.Rloss = 0; fmt.Gloss = 0; fmt.Bloss = 0; fmt.Aloss = 0; fmt.Rshift = 0; fmt.Gshift = 8; fmt.Bshift = 16; fmt.Ashift = 24; fmt.colorkey = 0; fmt.alpha = 0; surface2 = SDL_ConvertSurface (surface, &fmt, SDL_SWSURFACE); assert (surface2->pitch == surface2->w * 4); EXTEND (SP, 5); PUSHs (sv_2mortal (newSViv (surface2->w))); PUSHs (sv_2mortal (newSViv (surface2->h))); SDL_LockSurface (surface2); PUSHs (sv_2mortal (newSVpvn (surface2->pixels, surface2->h * surface2->pitch))); SDL_UnlockSurface (surface2); PUSHs (sv_2mortal (newSViv (surface->flags & (SDL_SRCCOLORKEY | SDL_SRCALPHA) ? GL_RGBA : GL_RGB))); PUSHs (sv_2mortal (newSViv (GL_RGBA))); PUSHs (sv_2mortal (newSViv (GL_UNSIGNED_BYTE))); SDL_FreeSurface (surface); SDL_FreeSurface (surface2); } void average (int x, int y, uint32_t *data) PPCODE: { uint32_t r = 0, g = 0, b = 0, a = 0; x = y = x * y; while (x--) { uint32_t p = *data++; r += (p ) & 255; g += (p >> 8) & 255; b += (p >> 16) & 255; a += (p >> 24) & 255; } EXTEND (SP, 4); PUSHs (sv_2mortal (newSViv (r / y))); PUSHs (sv_2mortal (newSViv (g / y))); PUSHs (sv_2mortal (newSViv (b / y))); PUSHs (sv_2mortal (newSViv (a / y))); } void fatal (char *message) CODE: #ifdef _WIN32 MessageBox (0, message, "Crossfire+ Fatal Error", MB_OK | MB_ICONERROR | MB_SETFOREGROUND); #else fprintf (stderr, "FATAL: %s\n", message); #endif exit (1); MODULE = CFClient PACKAGE = CFClient::Layout CFClient::Layout new (SV *class, int base_height = 10) CODE: New (0, RETVAL, 1, struct cf_layout); RETVAL->base_height = base_height; RETVAL->pl = pango_layout_new (context); pango_layout_set_wrap (RETVAL->pl, PANGO_WRAP_WORD_CHAR); pango_layout_set_font_description (RETVAL->pl, pango_font_description_copy ( pango_context_get_font_description (context))); OUTPUT: RETVAL void DESTROY (CFClient::Layout self) CODE: g_object_unref (self->pl); Safefree (self); void set_text (CFClient::Layout self, SV *text_) CODE: { STRLEN textlen; char *text = SvPVutf8 (text_, textlen); pango_layout_set_text (self->pl, text, textlen); } void set_markup (CFClient::Layout self, SV *text_) CODE: { STRLEN textlen; char *text = SvPVutf8 (text_, textlen); pango_layout_set_markup (self->pl, text, textlen); } SV * get_text (CFClient::Layout self) CODE: RETVAL = newSVpv (pango_layout_get_text (self->pl), 0); SvUTF8_on (RETVAL); OUTPUT: RETVAL void set_height (CFClient::Layout self, int base_height) CODE: self->base_height = base_height; void set_width (CFClient::Layout self, int max_width = -1) CODE: pango_layout_set_width (self->pl, max_width < 0 ? max_width : max_width * PANGO_SCALE); void size (CFClient::Layout self) PPCODE: { int w, h; layout_update (self); layout_get_pixel_size (self, &w, &h); EXTEND (SP, 2); PUSHs (sv_2mortal (newSViv (w))); PUSHs (sv_2mortal (newSViv (h))); } int xy_to_index (CFClient::Layout self, int x, int y) CODE: { int index, trailing; layout_update (self); pango_layout_xy_to_index (self->pl, x * PANGO_SCALE, y * PANGO_SCALE, &index, &trailing); RETVAL = index; } OUTPUT: RETVAL void cursor_pos (CFClient::Layout self, int index) PPCODE: { PangoRectangle strong_pos; layout_update (self); pango_layout_get_cursor_pos (self->pl, index, &strong_pos, 0); EXTEND (SP, 3); PUSHs (sv_2mortal (newSViv (strong_pos.x / PANGO_SCALE))); PUSHs (sv_2mortal (newSViv (strong_pos.y / PANGO_SCALE))); PUSHs (sv_2mortal (newSViv (strong_pos.height / PANGO_SCALE))); } void render (CFClient::Layout self) PPCODE: { SV *retval; int w, h; FT_Bitmap bitmap; layout_update (self); layout_get_pixel_size (self, &w, &h); retval = newSV (w * h); SvPOK_only (retval); SvCUR_set (retval, w * h); bitmap.rows = h; bitmap.width = w; bitmap.pitch = w; bitmap.buffer = (unsigned char*)SvPVX (retval); bitmap.num_grays = 256; bitmap.pixel_mode = FT_PIXEL_MODE_GRAY; memset (bitmap.buffer, 0, w * h); pango_ft2_render_layout (&bitmap, self->pl, 0 * PANGO_SCALE, 0 * PANGO_SCALE); EXTEND (SP, 3); PUSHs (sv_2mortal (newSViv (w))); PUSHs (sv_2mortal (newSViv (h))); PUSHs (sv_2mortal (retval)); } MODULE = CFClient PACKAGE = CFClient::Texture void draw_quad (SV *self, float x, float y, float w = 0, float h = 0) PROTOTYPE: $$$;$$ CODE: { HV *hv = (HV *)SvRV (self); float s = SvNV (*hv_fetch (hv, "s", 1, 1)); float t = SvNV (*hv_fetch (hv, "t", 1, 1)); int name = SvIV (*hv_fetch (hv, "name", 4, 1)); int wrap_mode = SvIV (*hv_fetch (hv, "wrap_mode", 9, 1)); if (items < 5) { w = SvNV (*hv_fetch (hv, "w", 1, 1)); h = SvNV (*hv_fetch (hv, "h", 1, 1)); } glBindTexture (GL_TEXTURE_2D, name); if (wrap_mode) { glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } glBegin (GL_QUADS); glTexCoord2f (0, 0); glVertex2f (x , y ); glTexCoord2f (0, t); glVertex2f (x , y + h); glTexCoord2f (s, t); glVertex2f (x + w, y + h); glTexCoord2f (s, 0); glVertex2f (x + w, y ); glEnd (); } MODULE = CFClient PACKAGE = CFClient::Map CFClient::Map new (SV *class, int map_width, int map_height) CODE: New (0, RETVAL, 1, struct map); RETVAL->x = 0; RETVAL->y = 0; RETVAL->w = map_width; RETVAL->h = map_height; RETVAL->ox = 0; RETVAL->oy = 0; RETVAL->faces = 8192; Newz (0, RETVAL->face, RETVAL->faces, mapface); RETVAL->texs = 8192; Newz (0, RETVAL->tex, RETVAL->texs, maptex); RETVAL->rows = 0; RETVAL->row = 0; OUTPUT: RETVAL void DESTROY (CFClient::Map self) CODE: { map_clear (self); Safefree (self->face); Safefree (self); } void clear (CFClient::Map self) CODE: map_clear (self); void set_face (CFClient::Map self, int face, int texid) CODE: { while (self->faces <= face) { Append (mapface, self->face, self->faces, self->faces); self->faces *= 2; } self->face [face] = texid; } void set_texture (CFClient::Map self, int texid, int name, int w, int h, float s, float t, int r, int g, int b, int a) CODE: { while (self->texs <= texid) { Append (maptex, self->tex, self->texs, self->texs); self->texs *= 2; } { maptex *tex = self->tex + texid; tex->name = name; tex->w = w; tex->h = h; tex->s = s; tex->t = t; tex->r = r; tex->g = g; tex->b = b; tex->a = a; } } int ox (CFClient::Map self) ALIAS: oy = 1 CODE: switch (ix) { case 0: RETVAL = self->ox; break; case 1: RETVAL = self->oy; break; } OUTPUT: RETVAL void scroll (CFClient::Map self, int dx, int dy) CODE: { if (dx > 0) map_blank (self, self->x, self->y, dx - 1, self->h); else if (dx < 0) map_blank (self, self->x + self->w + dx + 1, self->y, 1 - dx, self->h); if (dy > 0) map_blank (self, self->x, self->y, self->w, dy - 1); else if (dy < 0) map_blank (self, self->x, self->y + self->h + dy + 1, self->w, 1 - dy); self->ox += dx; self->x += dx; self->oy += dy; self->y += dy; while (self->y < 0) { Prepend (maprow, self->row, self->rows, MAP_EXTEND_Y); self->rows += MAP_EXTEND_Y; self->y += MAP_EXTEND_Y; } } void map1a_update (CFClient::Map self, SV *data_) CODE: { uint8_t *data = (uint8_t *)SvPVbyte_nolen (data_); uint8_t *data_end = (uint8_t *)SvEND (data_); mapcell *cell; int x, y, flags; while (data < data_end) { flags = (data [0] << 8) + data [1]; data += 2; x = ((flags >> 10) & 63) + self->x; y = ((flags >> 4) & 63) + self->y; cell = map_get_cell (self, x, y); if (flags & 15) { if (cell->darkness < 0) { cell->darkness = 0; cell->face [0] = 0; cell->face [1] = 0; cell->face [2] = 0; } cell->darkness = flags & 8 ? *data++ : 255; //TODO: don't trust server data to be in-range(!) if (flags & 4) { cell->face [0] = self->face [(data [0] << 8) + data [1]]; data += 2; } if (flags & 2) { cell->face [1] = self->face [(data [0] << 8) + data [1]]; data += 2; } if (flags & 1) { cell->face [2] = self->face [(data [0] << 8) + data [1]]; data += 2; } } else cell->darkness = -1; } } SV * mapmap (CFClient::Map self, int x0, int y0, int w, int h) CODE: { int x1, x; int y1, y; int z; SV *map_sv = newSV (w * h * sizeof (uint32_t)); uint32_t *map = (uint32_t *)SvPVX (map_sv); SvPOK_only (map_sv); SvCUR_set (map_sv, w * h * sizeof (uint32_t)); x0 += self->x; x1 = x0 + w; y0 += self->y; y1 = y0 + h; for (y = y0; y < y1; y++) { maprow *row = 0 <= y && y < self->rows ? self->row + y : 0; for (x = x0; x < x1; x++) { int r = 32, g = 32, b = 32, a = 192; if (row && row->c0 <= x && x < row->c1) { mapcell *cell = row->col + (x - row->c0); for (z = 0; z <= 0; z++) { mapface face = cell->face [z]; if (face) { maptex tex = self->tex [face]; int a0 = 255 - tex.a; int a1 = tex.a; r = (r * a0 + tex.r * a1) / 255; g = (g * a0 + tex.g * a1) / 255; b = (b * a0 + tex.b * a1) / 255; a = (a * a0 + tex.a * a1) / 255; } } } *map++ = (r ) | (g << 8) | (b << 16) | (a << 24); } } RETVAL = map_sv; } OUTPUT: RETVAL void draw (CFClient::Map self, int shift_x, int shift_y, int x0, int y0, int sw, int sh) PPCODE: { int vx, vy; int x, y, z; int last_name; mapface face; int sw4 = (sw + 3) & ~3; SV *darkness_sv = sv_2mortal (newSV (sw4 * sh)); uint8_t *darkness = (uint8_t *)SvPVX (darkness_sv); memset (darkness, 255, sw4 * sh); SvPOK_only (darkness_sv); SvCUR_set (darkness_sv, sw4 * sh); vx = self->x + (self->w - sw) / 2 - shift_x; vy = self->y + (self->h - sh) / 2 - shift_y; /* int vx = self->vx = self->w >= sw ? self->x + (self->w - sw) / 2 : MIN (self->x, MAX (self->x + self->w - sw + 1, self->vx)); int vy = self->vy = self->h >= sh ? self->y + (self->h - sh) / 2 : MIN (self->y, MAX (self->y + self->h - sh + 1, self->vy)); */ glColor4ub (255, 255, 255, 255); glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable (GL_BLEND); glEnable (GL_TEXTURE_2D); glTexEnvi (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); glBegin (GL_QUADS); last_name = 0; for (z = 0; z < 3; z++) for (y = 0; y < sh; y++) if (0 <= y + vy && y + vy < self->rows) { maprow *row = self->row + (y + vy); for (x = 0; x < sw; x++) if (row->c0 <= x + vx && x + vx < row->c1) { mapcell *cell = row->col + (x + vx - row->c0); darkness[y * sw4 + x] = cell->darkness < 0 ? 255 - FOW_DARKNESS : 255 - cell->darkness; face = cell->face [z]; if (face) { maptex tex = self->tex [face]; int px = (x + 1) * 32 - tex.w; int py = (y + 1) * 32 - tex.h; if (last_name != tex.name) { glEnd (); last_name = tex.name; glBindTexture (GL_TEXTURE_2D, last_name); glBegin (GL_QUADS); } glTexCoord2f (0 , 0 ); glVertex2f (px , py ); glTexCoord2f (0 , tex.t); glVertex2f (px , py + tex.h); glTexCoord2f (tex.s, tex.t); glVertex2f (px + tex.w, py + tex.h); glTexCoord2f (tex.s, 0 ); glVertex2f (px + tex.w, py ); } } } glEnd (); glDisable (GL_TEXTURE_2D); glDisable (GL_BLEND); EXTEND (SP, 3); PUSHs (sv_2mortal (newSViv (sw4))); PUSHs (sv_2mortal (newSViv (sh))); PUSHs (darkness_sv); } SV * get_rect (CFClient::Map self, int x0, int y0, int w, int h) CODE: { int x, y, x1, y1; SV *data_sv = newSV (w * h * 7 + 5); uint8_t *data = (uint8_t *)SvPVX (data_sv); *data++ = 0; /* version 0 format */ *data++ = w >> 8; *data++ = w; *data++ = h >> 8; *data++ = h; // we need to do this 'cause we don't keep an absolute coord system for rows // TODO: treat rows as we treat columns map_get_row (self, y0 + self->y - self->oy);//D map_get_row (self, y0 + self->y - self->oy + h - 1);//D x0 += self->x - self->ox; y0 += self->y - self->oy; x1 = x0 + w; y1 = y0 + h; for (y = y0; y < y1; y++) { maprow *row = 0 <= y && y < self->rows ? self->row + y : 0; for (x = x0; x < x1; x++) { if (row && row->c0 <= x && x < row->c1) { mapcell *cell = row->col + (x - row->c0); uint8_t flags = 0; if (cell->face [0]) flags |= 1; if (cell->face [1]) flags |= 2; if (cell->face [2]) flags |= 4; *data++ = flags; if (flags & 1) { *data++ = cell->face [0] >> 8; *data++ = cell->face [0]; } if (flags & 2) { *data++ = cell->face [1] >> 8; *data++ = cell->face [1]; } if (flags & 4) { *data++ = cell->face [2] >> 8; *data++ = cell->face [2]; } } else *data++ = 0; } } SvPOK_only (data_sv); SvCUR_set (data_sv, data - (uint8_t *)SvPVX (data_sv)); RETVAL = data_sv; } OUTPUT: RETVAL void set_rect (CFClient::Map self, int x0, int y0, uint8_t *data) PPCODE: { int x, y, z; int w, h; int x1, y1; if (*data++ != 0) return; /* version mismatch */ w = *data++ << 8; w |= *data++; h = *data++ << 8; h |= *data++; // we need to do this 'cause we don't keep an absolute coord system for rows // TODO: treat rows as we treat columns map_get_row (self, y0 + self->y - self->oy);//D map_get_row (self, y0 + self->y - self->oy + h - 1);//D x0 += self->x - self->ox; y0 += self->y - self->oy; x1 = x0 + w; y1 = y0 + h; for (y = y0; y < y1; y++) { maprow *row = map_get_row (self, y); for (x = x0; x < x1; x++) { uint8_t flags = *data++; if (flags) { mapface face[3] = { 0, 0, 0 }; mapcell *cell = row_get_cell (row, x); if (flags & 1) { face[0] = *data++ << 8; face[0] |= *data++; } if (flags & 2) { face[1] = *data++ << 8; face[1] |= *data++; } if (flags & 4) { face[2] = *data++ << 8; face[2] |= *data++; } if (cell->darkness <= 0) { cell->darkness = -1; for (z = 0; z <= 2; z++) { cell->face[z] = face[z]; if (face[z] && (face[z] >= self->texs || !self->tex[face [z]].name)) XPUSHs (sv_2mortal (newSViv (face[z]))); } } } } } } MODULE = CFClient PACKAGE = CFClient::MixChunk CFClient::MixChunk new_from_file (SV *class, char *path) CODE: RETVAL = Mix_LoadWAV (path); OUTPUT: RETVAL void DESTROY (CFClient::MixChunk self) CODE: Mix_FreeChunk (self); int volume (CFClient::MixChunk self, int volume = -1) CODE: RETVAL = Mix_VolumeChunk (self, volume); OUTPUT: RETVAL int play (CFClient::MixChunk self, int channel = -1, int loops = 0, int ticks = -1) CODE: RETVAL = Mix_PlayChannelTimed (channel, self, loops, ticks); OUTPUT: RETVAL MODULE = CFClient PACKAGE = CFClient::MixMusic int volume (int volume = -1) CODE: RETVAL = Mix_VolumeMusic (volume); OUTPUT: RETVAL CFClient::MixMusic new_from_file (SV *class, char *path) CODE: RETVAL = Mix_LoadMUS (path); OUTPUT: RETVAL void DESTROY (CFClient::MixMusic self) CODE: Mix_FreeMusic (self); int play (CFClient::MixMusic self, int loops = -1) CODE: RETVAL = Mix_PlayMusic (self, loops); OUTPUT: RETVAL MODULE = CFClient PACKAGE = CFClient::OpenGL BOOT: { HV *stash = gv_stashpv ("CFClient::OpenGL", 1); static const struct { const char *name; IV iv; } *civ, const_iv[] = { # define const_iv(name) { # name, (IV)name } const_iv (GL_COLOR_MATERIAL), const_iv (GL_SMOOTH), const_iv (GL_FLAT), const_iv (GL_BLEND), const_iv (GL_AND), const_iv (GL_SRC_ALPHA), const_iv (GL_ONE_MINUS_SRC_ALPHA), const_iv (GL_RGB), const_iv (GL_RGBA), const_iv (GL_UNSIGNED_BYTE), const_iv (GL_ALPHA4), const_iv (GL_ALPHA), const_iv (GL_FLOAT), const_iv (GL_UNSIGNED_INT_8_8_8_8_REV), const_iv (GL_COMPILE), const_iv (GL_TEXTURE_1D), const_iv (GL_TEXTURE_2D), const_iv (GL_TEXTURE_ENV), const_iv (GL_TEXTURE_MAG_FILTER), const_iv (GL_TEXTURE_MIN_FILTER), const_iv (GL_TEXTURE_ENV_MODE), const_iv (GL_TEXTURE_WRAP_S), const_iv (GL_TEXTURE_WRAP_T), const_iv (GL_CLAMP), const_iv (GL_REPEAT), const_iv (GL_NEAREST), const_iv (GL_LINEAR), const_iv (GL_NEAREST_MIPMAP_NEAREST), const_iv (GL_LINEAR_MIPMAP_NEAREST), const_iv (GL_NEAREST_MIPMAP_LINEAR), const_iv (GL_LINEAR_MIPMAP_LINEAR), const_iv (GL_GENERATE_MIPMAP), const_iv (GL_LINEAR), const_iv (GL_MODULATE), const_iv (GL_REPLACE), const_iv (GL_COLOR_BUFFER_BIT), const_iv (GL_PROJECTION), const_iv (GL_MODELVIEW), const_iv (GL_COLOR_LOGIC_OP), const_iv (GL_CONVOLUTION_2D), const_iv (GL_CONVOLUTION_BORDER_MODE), const_iv (GL_CONSTANT_BORDER), const_iv (GL_LINES), const_iv (GL_QUADS), const_iv (GL_LINE_LOOP), const_iv (GL_PERSPECTIVE_CORRECTION_HINT), const_iv (GL_FASTEST), # undef const_iv }; for (civ = const_iv + sizeof (const_iv) / sizeof (const_iv [0]); civ-- > const_iv; ) newCONSTSUB (stash, (char *)civ->name, newSViv (civ->iv)); } int glGetError () void glClear (int mask) void glClearColor (float r, float g, float b, float a = 1.0) PROTOTYPE: @ void glEnable (int cap) void glDisable (int cap) void glShadeModel (int mode) void glHint (int target, int mode) void glBlendFunc (int sfactor, int dfactor) void glLogicOp (int opcode) void glMatrixMode (int mode) void glPushMatrix () void glPopMatrix () void glLoadIdentity () void glOrtho (double left, double right, double bottom, double top, double near, double far) void glViewport (int x, int y, int width, int height) void glTranslate (float x, float y, float z = 0.) CODE: glTranslatef (x, y, z); void glScale (float x, float y, float z) CODE: glScalef (x, y, z); void glRotate (float angle, float x, float y, float z) CODE: glRotatef (angle, x, y, z); void glBegin (int mode) void glEnd () void glColor (float r, float g, float b, float a = 1.0) PROTOTYPE: @ CODE: glColor4f (r, g, b, a); void glVertex (float x, float y, float z = 0.) CODE: glVertex3f (x, y, z); void glTexCoord (float s, float t) CODE: glTexCoord2f (s, t); void glTexEnv (int target, int pname, float param) CODE: glTexEnvf (target, pname, param); void glTexParameter (int target, int pname, float param) CODE: glTexParameterf (target, pname, param); void glBindTexture (int target, int name) void glConvolutionParameter (int target, int pname, float params) CODE: glConvolutionParameterf (target, pname, params); void glConvolutionFilter2D (int target, int internalformat, int width, int height, int format, int type, char *data) void glTexImage2D (int target, int level, int internalformat, int width, int height, int border, int format, int type, char *data) void glCopyTexImage2D (int target, int level, int internalformat, int x, int y, int width, int height, int border) int glGenTexture () CODE: { GLuint name; glGenTextures (1, &name); RETVAL = name; } OUTPUT: RETVAL void glDeleteTexture (int name) CODE: { GLuint name_ = name; glDeleteTextures (1, &name_); } int glGenList () CODE: RETVAL = glGenLists (1); OUTPUT: RETVAL void glDeleteList (int list) CODE: glDeleteLists (list, 1); void glNewList (int list, int mode = GL_COMPILE) void glEndList () void glCallList (int list)