0001 #define mp_cnt 1
0002 #include "AnimDC.HC"
0003 #include "BlobFile.HC"
0004 #include "SmallTalk/Load.HC"
0005 #include "Intersect.HC"
0006 #include "QuestEditor.HC";
0007 #include "PathFinder.HC";
0008 #define TEST_3D
0009 #ifndef ENGINE_3D
0010 #include "SSE.HC";
0011 #define VIEW_DIST 32
0012 #define FOG_DIVISIONS 0x10
0013 #define FOG_DIST (128*4.)
0014 U64 fog_table[COLORS_NUM][COLORS_NUM][FOG_DIVISIONS];
0015 U0 ErectFogTable0(U64 *table,I64 fog_color,F64 ratio) {
0016   CBGR48 palette[COLORS_NUM];
0017   I64 idx,which;
0018   F64 r,g,b,dist,best_dist;
0019   F64 r2,g2,b2;
0020   U16 best_color,dither;
0021   GrPaletteGet(palette);
0022   for(idx=0;idx!=COLORS_NUM;idx++) {
0023     r=palette[idx].r*ratio+palette[fog_color].r*(1.-ratio);
0024     g=palette[idx].g*ratio+palette[fog_color].g*(1.-ratio);
0025     b=palette[idx].b*ratio+palette[fog_color].b*(1.-ratio);
0026     best_dist=1e100;
0027     for(dither=0;dither!=0xff;dither++) {
0028       r2=(palette[dither&0xf].r+palette[dither>>4].r)/2.;
0029       g2=(palette[dither&0xf].g+palette[dither>>4].g)/2.;
0030       b2=(palette[dither&0xf].b+palette [dither>>4].b)/2.;
0031       dist=Sqrt(Sqr(r-r2)+Sqr(g-g2)+Sqr(b-b2));
0032       if(dist<best_dist) {
0033         best_color=dither;
0034         best_dist=dist;
0035       }
0036     }
0037     table[idx].u8[0]=best_color&0xf;
0038     table[idx].u8[2]=best_color&0xf;
0039     table[idx].u8[4]=best_color&0xf;
0040     table[idx].u8[6]=best_color&0xf;
0041 
0042     table[idx].u8[1]=best_color>>4;
0043     table[idx].u8[3]=best_color>>4;
0044     table[idx].u8[5]=best_color>>4;
0045     table[idx].u8[7]=best_color>>4;
0046   }
0047 }
0048 U0 ErectFogTable(U64 *table,I64 divisions) {
0049   I64 f,d;
0050   for(d=0;d!=divisions;d++) {
0051     for(f=0;f!=16;f++)
0052       ErectFogTable0(&table[f*COLORS_NUM+d*COLORS_NUM*FOG_DIVISIONS],f,d/ToF64(divisions));
0053   }
0054 }
0055 ErectFogTable(fog_table,0x10);
0056 U0 FrogImgBlank(CFrogImg *img) {
0057   img->dc=DCNew(1,1,frog_mem_task);
0058   img->name=FrogStrNew("");
0059 }
0060 U0 FrogImgSetGraphics(CFrogImg *img,I64 *argv,I64 argc) {
0061   U8 *lunk=AsString(argv[0]),*data,*symbol_name;
0062   if(img->dc)
0063     AnimDCDel(img->dc);
0064   img->dc=AnimDCNew(1,1,frog_mem_task);
0065   if(lunk) {
0066     if(data=BlobFileGetLump(,lunk)) {
0067       AnimDCDel(img->dc);
0068       img->dc=AnimDCLoad(data,,frog_mem_task);
0069       img->name=FrogStrNew(lunk);
0070       Free(data);
0071     }
0072     Free(lunk);
0073   }
0074   return FROG_SMALL_NIL;
0075 }
0076 AddMethod("CFrogImg","setGraphics:",&FrogImgSetGraphics);
0077 CFrogNum *FrogImgWidth(CFrogImg *img,I64 *argv,I64 argc) {
0078   if(img->dc)
0079     return FrogNumNew(img->dc->width);
0080   return FROG_SMALL_NIL;
0081 }
0082 AddMethod("CFrogImg","width",&FrogImgWidth);
0083 CFrogNum *FrogImgHeight(CFrogImg *img,I64 *argv,I64 argc) {
0084   if(img->dc)
0085     return FrogNumNew(img->dc->height);
0086   return FROG_SMALL_NIL;
0087 }
0088 AddMethod("CFrogImg","height",&FrogImgHeight);
0089 #include "TextureEditor";
0090 CFrogImg *FrogImgNewFromEditor(CFrogImg *img,I64 *argv,I64 argc) {
0091   U8 *lump;
0092   lump=TextureEdit(dft_blob_path);
0093   CFrogImg *ret=FROG_SMALL_NIL;
0094   if(lump) {
0095     ret=CallScript("get:",img,FrogStrNew(lump)); //Get an existing name/replace
0096     CallScript("setGraphics:",ret,FrogStrNew(lump));
0097     Free(lump);
0098   }
0099   return ret;
0100 }
0101 AddClassMethod("CFrogImg","newFromEditor",&FrogImgNewFromEditor);
0102 U0 FrogImgDel(CFrogImg *img) {
0103   AnimDCDel(img->dc);
0104   Free(img);
0105 }
0106 
0107 CFrogStr *FrogImgToString(CFrogImg *img,I64 *argv,I64 argc) {
0108   CFrogStr *s=ConstructThing("CFrogStr");
0109   I64 len;
0110   U8 *a=AnimDCSave(img->dc,&len);
0111   U8SetAddBytes(s->items,a,len);
0112   Free(a);
0113   return s;
0114 }
0115 AddMethod("CFrogImg","asString",&FrogImgToString);
0116 CFrogStr *FrogImgFromString(CFrogImg *img,I64 *argv,I64 argc) {
0117   CFrogStr *s=argv[0];
0118   CDC *d=NULL;
0119   if(s) {
0120     d=AnimDCLoad(s->items->body,,frog_mem_task);
0121     AnimDCDel(img->dc);
0122     img->dc=d;
0123   }
0124   return FROG_SMALL_NIL;
0125 }
0126 AddMethod("CFrogImg","setGraphicsFromString:",&FrogImgFromString);
0127 
0128 
0129 U64 Fog(I64 color,U64 dist,U8 fcolor=BLACK) {
0130   dist/=FOG_DIST;
0131   if(dist<FOG_DIVISIONS) {
0132     return fog_table[dist][color][fcolor];
0133   }
0134   return fcolor;
0135 }
0136 Bool IsLiquidTile(I64 t) {
0137   switch(t) {
0138     case 6: //water
0139     case 10: //lava
0140       return TRUE;
0141   }
0142   return FALSE;
0143 }
0144 extern I64 *GetTilesInPath(U8 *w,F64 angle,F64 dist);
0145 #define ENGINE_3D 1
0146 #define SCRN_TURN_MARGIN 100
0147 #define SCRN_TURN_RATE (pi/30./7)
0148 //This will delegate calls to other cores
0149 class CCSPair {
0150   U8 *name;
0151   CFrogThing *thing;
0152   CHashTable *ht;
0153 };
0154 //
0155 // From LevelEditor.HC
0156 //
0157 class CFilePtrLen {
0158   I32 ptr;
0159   I32 len;
0160 };
0161 class CFileTexture {
0162   I64 idx;
0163   U8 blob_name[STR_LEN];
0164   U8 wall_blob_name[STR_LEN];
0165   U8 ceil_blob_name[STR_LEN];
0166 };
0167 
0168 class CFileThingTemplate {
0169   I16 idx;
0170   U8 name[STR_LEN];
0171   U8 data[STR_LEN];
0172   U8 blob_side_name[STR_LEN];
0173   U8 blob_front_name[STR_LEN];
0174   U8 blob_back_name[STR_LEN];
0175 };
0176 class CFileThing {
0177   U16 thing_template_idx;
0178 //x/y are in tiles 
0179   F64 x,y,rot;
0180   U8 chat_bot_name[32];
0181   U8 drop_item_name[32];
0182   U8 activate_tag[32];
0183   U8 pad[32];
0184 };
0185 #define DOORF_SMALL_KEY 1
0186 #define DOORF_BIG_KEY 2
0187 #define DOORF_TO_CEILING 4
0188 #define DOORF_SIDE_DOOR 8
0189 class CFileDoor {
0190   I64 x,y,act_as_wall;
0191   F64 height;
0192   I32 flags;
0193   I32 tag;
0194 };
0195 
0196 class C3DParticle:CQue {
0197   F64 x,y,z,born_at;
0198   F64 momx,momy,momz;
0199   I32 color,size;
0200   U8 splat_lump_name[STR_LEN];
0201   Bool hit;
0202 };
0203 
0204 I64 class CWallCommand {
0205   I16 start,end;
0206   I32 dist;
0207 };
0208 
0209 #define GRID_SZ 128
0210 #define THING_RADIUS (GRID_SZ/3)
0211 #define THING_HEIGHT 1.
0212 #define DIST_SCALE (GR_HEIGHT*GRID_SZ)
0213 #define EMPTY_SPACE 0
0214 class C3DWorld {
0215   F64 fov,x,y,h,angle;
0216   F64 angle2;
0217   I64 step_width,step_width_y;
0218   I64 world_width;
0219   I64 world_height;
0220   I8 *world_blocks; //x*y*256+z
0221   I8 *world_lighting; //x*y*256+z
0222   CDC *wall_textures[0x10000/2],*to_dc;
0223   CDC *floor_textures[0x10000/2];
0224   CDC *floor_textures1616[0x10000/2];
0225   CDC *ceil_textures[0x10000/2];
0226   C3DThingTemplate *thing_templates[0x10000];
0227   I64 mp_done,dc_lock;
0228   CQue _2d_thing_templates; 
0229   CI64Set *_2d_things;
0230   CI64Set *_wall_stains;
0231   CI64Set *_floor_stains;
0232   CTask *mem_task;
0233   F64 cam_height;
0234 //Private
0235   CQue particles;
0236   I64 particle_cnt;
0237   CD3 to_point[GR_WIDTH/4][GR_HEIGHT/4];
0238   I64 things_around_camera_origin_x;
0239   I64 things_around_camera_origin_y;
0240   CI64Set *stains_around_camera[VIEW_DIST*2][VIEW_DIST*2];
0241 };
0242 C3DWorld *w;
0243 #define MAX_PARTICLES 128
0244 U0 NewParticle(C3DWorld *w,I64 color,I64 sz,F64 x,F64 y,F64 z,F64 mx,F64 my,F64 mz,U8 *splat_lump_name=NULL) {
0245   C3DParticle *p;
0246   if(w->particle_cnt>=MAX_PARTICLES) {
0247     w->particle_cnt--;
0248     QueRem(p=w->particles.next);
0249     Free(p);
0250   }
0251   p=CAlloc(sizeof(C3DParticle),w->mem_task);
0252   p->color=color;
0253   p->size=sz;
0254   p->x=x;
0255   p->y=y;
0256   p->z=z;
0257   p->momx=mx;
0258   p->momy=my;
0259   p->momz=mz;
0260   p->born_at=Frog_tS;
0261   if(splat_lump_name)
0262     StrCpy(p->splat_lump_name,splat_lump_name);
0263   QueIns(p,w->particles.last);
0264   w->particle_cnt++;
0265 }
0266 #define THINGF_FACE_FORWARD 1
0267 U0 C3DWorldSetFov(C3DWorld *w,F64 fov=pi/2) {
0268   w->fov=fov;
0269 }
0270 
0271 #define BLOB_THINGS_ROOT "Level/Things"
0272 #define BLOB_TILES_ROOT "Level/Tiles"
0273 
0274 CFrogThing *ThingTemplateSave(C3DThingTemplate *t,I64 *argv,I64 argc) {
0275   CFileThingTemplate f_template;
0276   U8 *name,*name2=NULL;
0277   if(ThingHasClass(t,"C3DThingTemplate")) {
0278     MemSet(&f_template,0,sizeof CFileThingTemplate);
0279     if(name=AsString(t->name)) {
0280       name2=MStrPrint(BLOB_THINGS_ROOT"/%s",name);
0281       StrCpy(f_template.name,name);
0282       Free(name);
0283     }
0284     if(ThingHasClass(t->front,"CFrogImg"))
0285       if(name=AsString(t->front->name)) {
0286         StrCpy(f_template.blob_front_name,name);
0287         Free(name);
0288       }
0289     if(ThingHasClass(t->back,"CFrogImg"))
0290       if(name=AsString(t->back->name)) {
0291         StrCpy(f_template.blob_back_name,name);
0292         Free(name);
0293       }
0294     if(ThingHasClass(t->side,"CFrogImg"))
0295       if(name=AsString(t->side->name)) {
0296         StrCpy(f_template.blob_side_name,name);
0297         Free(name);
0298       }
0299     if(name2) BlobFileAddLump(,name2,&f_template,sizeof(CFileThingTemplate));
0300     Free(name2);
0301   }
0302   return FROG_SMALL_NIL;
0303 }
0304 AddMethod("C3DThingTemplate","save",&ThingTemplateSave);
0305 
0306 
0307 CFrogThing *TileTemplateSave(CTileTemplate *t,I64 *argv,I64 argc) {
0308   CFileTexture f_template;
0309   U8 *name;
0310   if(ThingHasClass(t,"CTileTemplate")) {
0311     MemSet(&f_template,0,sizeof CFileTexture);
0312     f_template.idx=t->tile_idx;
0313     if(ThingHasClass(t->floor_texture,"CFrogImg"))
0314       if(name=AsString(t->floor_texture->name)) {
0315         StrCpy(f_template.blob_name,name);
0316         Free(name);
0317       }
0318     if(ThingHasClass(t->wall_texture,"CFrogImg"))
0319       if(name=AsString(t->wall_texture->name)) {
0320         StrCpy(f_template.wall_blob_name,name);
0321         Free(name);
0322       }
0323     if(ThingHasClass(t->ceil_texture,"CFrogImg"))
0324       if(name=AsString(t->ceil_texture->name)) {
0325         StrCpy(f_template.ceil_blob_name,name);
0326         Free(name);
0327       }
0328     name=MStrPrint(BLOB_TILES_ROOT"/%d",t->tile_idx);
0329     BlobFileAddLump(,name,&f_template,sizeof(CFileTexture));
0330     Free(name);
0331   }
0332   return FROG_SMALL_NIL;
0333 }
0334 AddMethod("CTileTemplate","save",&TileTemplateSave);
0335 C3DWorld *InitWorld(U8 *blob_file="ASS",F64 fov=pi/2) {
0336   C3DWorld *ret=CAlloc(sizeof(C3DWorld));
0337   F64 xoff,yoff;
0338   U8 *tmp;
0339   I64 idx,tile,stop_at;
0340   CDC *unscaled;
0341   U16 *world_mat;
0342   C3DThingTemplate **templates=ret->thing_templates,*template;
0343   C3DThing *thing;
0344   C3DWorldManager *man;
0345   C3DWorldSetFov(ret,fov);
0346   CTileTemplate *tile_template;
0347   CDoor *door;
0348   QueInit(&ret->particles);
0349   ret->step_width=4; 
0350   ret->step_width_y=4;
0351   ret->world_width=64;
0352   ret->world_height=64;
0353   ret->world_blocks=CAlloc(ret->world_width*ret->world_height*256);
0354   ret->world_lighting=CAlloc(ret->world_width*ret->world_height*256);
0355   w=ret;
0356   QueInit(&ret->_2d_thing_templates);
0357 
0358   man=ConstructThing("C3DWorldManager","world_handle",ret);
0359   CallScript("init",man);
0360   ret->_wall_stains=man->wall_stains->items;
0361   ret->_floor_stains=man->floor_stains->items;
0362   ret->_2d_things=man->things->items;
0363 
0364   CBlobFileHeader *bf_data=GetBlobFileData(blob_file); //Dont Free
0365   CLumpHeader *bf_lump=bf_data->lump_table;
0366   CFileThingTemplate *f_template;
0367   idx=bf_data->lump_cnt;
0368   while(--idx>=0) {
0369     if(!StrNCmp(bf_lump->name,BLOB_THINGS_ROOT,StrLen(BLOB_THINGS_ROOT))) {
0370       f_template=bf_data(U8*)+bf_lump->offset;
0371       tile=f_template->idx;
0372       if(I16_MIN<=tile<=I16_MAX) {
0373         template=ConstructThing("C3DThingTemplate");
0374         templates[f_template->idx-I16_MIN]=template;
0375         CallScript("setFront:",template,FrogStrNew(f_template->blob_front_name));
0376         CallScript("setBack:",template,FrogStrNew(f_template->blob_back_name));
0377         CallScript("setSide:",template,FrogStrNew(f_template->blob_side_name));
0378         CallScript("addThingTemplate:withName:",man,template,FrogStrNew(f_template->name));
0379       }
0380     }
0381     bf_lump++;
0382   }
0383   CFileTexture *ftxtr;
0384   bf_lump=bf_data->lump_table;
0385   idx=bf_data->lump_cnt;
0386   while(--idx>=0) {
0387     if(!StrNCmp(bf_lump->name,BLOB_TILES_ROOT,StrLen(BLOB_TILES_ROOT))) {
0388       ftxtr=bf_data(U8*)+bf_lump->offset;
0389       tile=AbsI64(ftxtr->idx);
0390       if(0<=tile<U8_MAX) {
0391         tile_template=ConstructThing("CTileTemplate");
0392         tile_template->tile_idx=tile;
0393         CallScript("setWall:",tile_template,FrogStrNew(ftxtr->wall_blob_name));
0394         CallScript("setFloor:",tile_template,FrogStrNew(ftxtr->blob_name));
0395         CallScript("setCeil:",tile_template,FrogStrNew(ftxtr->ceil_blob_name));
0396 
0397         if(ThingHasClass(tile_template->wall_texture,"CFrogImg")) {
0398           ret->wall_textures[tile]=ScaleDC(tile_template->wall_texture->dc,GRID_SZ,GRID_SZ);
0399         }
0400         if(ThingHasClass(tile_template->floor_texture,"CFrogImg")) {
0401           ret->floor_textures[tile]=ScaleDC(tile_template->floor_texture->dc,GRID_SZ,GRID_SZ);
0402         }
0403         if(ThingHasClass(tile_template->ceil_texture,"CFrogImg")) {
0404           ret->ceil_textures[tile]=ScaleDC(tile_template->ceil_texture->dc,GRID_SZ,GRID_SZ);
0405         }
0406         CallScript("addTileTemplate:",man,tile_template);
0407       }
0408     }
0409     bf_lump++;
0410   }
0411   return ret;
0412 }
0413 
0414 
0415 I64 BlockAtXYZ(I64 x,I64 y,I64 z) {
0416   I64 tile=x+y*w->world_width;
0417   if(0<=tile<w->world_height*w->world_width) {
0418     if(I8_MIN<=z<I8_MAX) {
0419       return w->world_blocks[tile*256+z-I8_MIN];
0420     }
0421   }
0422   return -1;
0423 }
0424 I64 LightAtXYZ(I64 x,I64 y,I64 z) {
0425   I64 tile=x+y*w->world_width;
0426   if(0<=tile<w->world_height*w->world_width) {
0427     if(I8_MIN<=z<I8_MAX) {
0428       return w->world_lighting[tile*256+z-I8_MIN];
0429     }
0430   }
0431   return 15;
0432 }
0433 
0434 Bool IsSolidTile(I64 t) {
0435   if(!t) return FALSE;
0436   return !IsLiquidTile(t);
0437 }
0438 #define LIGHT_DECAY 1.
0439 I8 BlockLightLevel(I64 t) {
0440   if(t==13)
0441     return 15;
0442   return 0;
0443 }
0444 Bool SetLightAtXYZ(I64 x,I64 y,I64 z,I64 light) {
0445   I64 tp=x+y*w->world_width;
0446   I64 l;
0447   if(IsSolidTile(BlockAtXYZ(x,y,z)))
0448     return FALSE;
0449   if(0<=x<w->world_width)
0450     if(0<=y<w->world_height)
0451       if(I8_MIN<=z<I8_MAX) {
0452         l=w->world_lighting[tp*256+z-I8_MIN];
0453         if(l<light) {
0454           w->world_lighting[tp*256+z-I8_MIN]=light;
0455           return TRUE;
0456         }
0457       }
0458   return FALSE;
0459 }
0460 F64 BlockAO(I64 which,I64 x,I64 y,I64 z,I64 sidex,I64 sidey,I64 sidez) {
0461   I64 cnt=0;
0462   static I64 offs1[6]={-1,0,1,0,-1,0};
0463   I64 corner=0;
0464   if(sidez) {
0465     if(BlockAtXYZ(x+offs1[which],y+offs1[1+which],z+sidez))
0466       cnt++;
0467     if(BlockAtXYZ(x+offs1[which+1],y+offs1[1+which+1],z+sidez))
0468       cnt++;
0469     if(cnt==2) return 0;
0470     return 3-corner-cnt;
0471   }
0472   if(sidex) {
0473     if(BlockAtXYZ(x+sidex,y+offs1[which],z+offs1[1+which]))
0474       cnt++;
0475     if(BlockAtXYZ(x+sidex,y+offs1[which+1],z+offs1[1+which+1]))
0476       cnt++;
0477     if(cnt==2) return 0;
0478     return 3-corner-cnt;
0479   }
0480   if(sidey) {
0481     if(BlockAtXYZ(x+offs1[which],y+sidey,z+offs1[1+which]))
0482       cnt++;
0483     if(BlockAtXYZ(x+offs1[which+1],y+sidey,z+offs1[1+which+1]))
0484       cnt++;
0485     if(cnt==2) return 0;
0486     return 3-corner-cnt;
0487   }
0488   return 0; 
0489 }
0490 
0491 F64 CornerLight(I64 which,I64 x,I64 y,I64 z,I64 sidex,I64 sidey,I64 sidez) {
0492   static I64 offs1[8]={-1,0,0,-1,1,0,0,1};
0493   which<<=1;
0494   if(sidez) {
0495     if(BlockAtXYZ(x+offs1[which],y+offs1[1+which],z+sidez))
0496       return LightAtXYZ(x,y,z+sidez);
0497     return LightAtXYZ(x+offs1[which],y+offs1[1+which],z+sidez);
0498   }
0499   if(sidey) {
0500     if(BlockAtXYZ(x+offs1[which],y+sidey,z+offs1[1+which]))
0501       return LightAtXYZ(x,y+sidey,z);
0502     return LightAtXYZ(x+offs1[which],y+sidey,z+offs1[1+which]);
0503   }
0504   if(sidex) {
0505     if(BlockAtXYZ(x+sidex,y+offs1[which],z+offs1[1+which]));
0506     return LightAtXYZ(x+sidex,y,z);
0507     return LightAtXYZ(x+sidex,y+offs1[which],z+offs1[1+which]);
0508   }
0509   return 0; 
0510 }
0511 
0512 
0513 U0 LightingLight(I64 x,I64 y,I64 z,I64 light) {
0514   CI64Set *stack=I64SetNew,*stack2=I64SetNew;
0515   CI64Set *visited=I64SetNew;
0516   I64 xo,yo,zo,dummy;
0517   I64 time=0;
0518   I64SetAdd(stack,x);
0519   I64SetAdd(stack,y);
0520   I64SetAdd(stack,z);
0521   do {
0522     while(stack->cnt>0) {
0523       z=stack->body[--stack->cnt];
0524       y=stack->body[--stack->cnt];
0525       x=stack->body[--stack->cnt];
0526       dummy.u16[0]=x;
0527       dummy.u16[1]=y;
0528       dummy.i16[2]=z;
0529       dummy.i16[3]=0;
0530       if(I64SetHas(visited,dummy))
0531         goto n;
0532       I64SetAdd(visited,dummy);
0533       for(zo=-1;zo<=1;zo+=2) {
0534         if(SetLightAtXYZ(x,y,z+zo,light)) {
0535           I64SetAdd(stack2,x);
0536           I64SetAdd(stack2,y);
0537           I64SetAdd(stack2,z+zo);
0538         }
0539       }
0540       for(xo=-1;xo<=1;xo+=2) {
0541         if(SetLightAtXYZ(x+xo,y,z,light)) {
0542           I64SetAdd(stack2,x+xo);
0543           I64SetAdd(stack2,y);
0544           I64SetAdd(stack2,z);
0545         }
0546       }
0547       for(yo=-1;yo<=1;yo+=2) {
0548         if(SetLightAtXYZ(x,y+yo,z,light)) {
0549           I64SetAdd(stack2,x);
0550           I64SetAdd(stack2,y+yo);
0551           I64SetAdd(stack2,z);
0552         }
0553       }
0554 n:;
0555     }
0556     SwapI64(&stack2,&stack);
0557   } while(--light>0);
0558   I64SetDel(visited);
0559   I64SetDel(stack2);
0560   I64SetDel(stack);
0561 }
0562 U0 LightingSky(I64 x,I64 y,I64 sky_light=16,I64 shadow_color=BLACK) {
0563   I64 z=I8_MAX,b;
0564   U8 *col;
0565   U8 *lcol;
0566   Bool sky=TRUE;
0567   F64 level=sky_light;
0568   I64 tp=x+y*w->world_width;
0569   if(0<=x<w->world_width)
0570     if(0<=y<w->world_height) {
0571       col=&w->world_blocks[tp*256+z-I8_MIN];
0572       lcol=&w->world_lighting[tp*256+z-I8_MIN];
0573       while(z>=I8_MIN) {
0574         b=col[z-I8_MIN];
0575         if(IsSolidTile(b)) {
0576           sky=FALSE;
0577         }
0578         if(!sky)
0579           level=0;
0580         lcol[z-I8_MIN]=MaxI64(ToI64(level),0);
0581         --z;
0582       }
0583     }
0584 }
0585 CFrogThing *WorldLightingSky(C3DWorldManager *man,I64 *argv,I64 argc) {
0586   I64 x=w->x/128.,ix;
0587   I64 y=w->y/128.,iy;
0588   I64 sky_light=AsF64(argv[0]),z,tp;
0589    I64 level;
0590 //Sky light
0591   for(ix=-VIEW_DIST*2;ix<VIEW_DIST*2;ix++) {
0592     for(iy=-VIEW_DIST*2;iy<VIEW_DIST*2;iy++) {
0593       LightingSky(ix+x,iy+y,sky_light);
0594     }
0595   }
0596 //Pass2 world lights
0597   for(ix=-VIEW_DIST*2;ix<VIEW_DIST*2;ix++) {
0598     for(iy=-VIEW_DIST*2;iy<VIEW_DIST*2;iy++) {
0599       z=I8_MAX;
0600       while(z>=I8_MIN) {
0601         if(1<=(level=BlockLightLevel(BlockAtXYZ(x+ix,y+iy,z)))) {
0602           LightingLight(x+ix,y+iy,z,level);
0603         }
0604         --z;
0605       }
0606     }
0607   }
0608 }
0609 AddMethod("C3DWorldManager","skylight:",&WorldLightingSky);
0610 Bool HeightImpossible(C3DWorld *w,I64 ix,I64 iy,F64 above,Bool allow_ceil,F64 tallness=.1,F64 wiggle_room=0) {
0611   I64 iz=Floor(above);
0612   I64 c,idx2,t;
0613   F64 h;
0614   if(!(-128<iz<128)) return FALSE;
0615   if(0>ix) return TRUE;
0616   if(w->world_width<=ix) return TRUE;
0617   if(0>iy) return TRUE;
0618   if(w->world_height<=iy) return TRUE;
0619   if(IsSolidTile(BlockAtXYZ(ix,iy,iz))) {
0620     return TRUE;
0621   }
0622   for(idx2=0;idx2<tallness;idx2++) {
0623     idx2++;
0624     if(IsSolidTile(BlockAtXYZ(ix,iy,iz+idx2)))
0625       return TRUE;
0626   }
0627   return FALSE;
0628 }
0629 
0630 C3DThing *BumpsIntoThing(C3DThing *self,I64 *argv,I64 argc) {
0631   C3DThing *who=argv[0];
0632   F64 radius=THING_RADIUS*THING_RADIUS;
0633   F64 x,y;
0634   x=who->x-self->x;
0635   y=who->y-self->y;
0636   if(Max(self->z,who->z)
0637         <=Min(self->z+THING_HEIGHT,who->z+THING_HEIGHT))
0638     if(radius>=x*x+y*y)
0639       return FrogNumNew(1);
0640   return FrogNumNew(0);
0641 } 
0642 AddMethod("C3DThing","hitsThing:",&BumpsIntoThing);
0643 CFrogNum *C3DThingPathFindWalkTowardsThing(C3DThing *self,I64 *argv,I64 argc) {
0644   CPFPoint pt;
0645   C3DThing *thing=argv[0];
0646   if(!ThingHasClass(thing,"C3DThing")) return FROG_SMALL_NIL;
0647   CI64Set *path=PathFinder(self->x/128,self->y/128,Floor(self->z),thing->x/128,thing->y/128,Floor(thing->z),&BlockAtXYZ,AsF64(argv[1]));
0648   if(path->cnt<2) {
0649     I64SetDel(path);
0650     return FrogNumNew(Arg(thing->x-self->x,thing->y-self->y));
0651   }
0652 //TODO account for large same direction
0653   pt=path->body[1];
0654   I64SetDel(path);
0655   return FrogNumNew(Arg(pt.x+.5-self->x/128,pt.y+.5-self->y/128));
0656 }
0657 AddMethod("C3DThing","pathFinderAngleTowardsThing:withJump:",&C3DThingPathFindWalkTowardsThing);
0658 #define RADF_CEIL 1
0659 #define RADF_FLOOR 2
0660 #define RADF_DOOR 4
0661 Bool RadiusHitsWall(C3DWorld *w,F64 angle,F64 *_x,F64 *_y,F64 height,F64 radius=THING_RADIUS,Bool adjust=FALSE,F64 tallness=.8,I64 flags=RADF_CEIL|RADF_FLOOR) {
0662   F64 x=*_x;
0663   F64 y=*_y;
0664   x/=GRID_SZ;
0665   y/=GRID_SZ;
0666   radius/=GRID_SZ;
0667   F64 by=1/4.;
0668   Bool ret=FALSE,hit=TRUE;
0669   I64 xoff,yoff,ch,cr,tile,i;
0670   F64 tx,ty,h;
0671   do {
0672     hit=FALSE;
0673     for(i=0;i!=4;i++)  {
0674       for(xoff=-1;xoff<2;xoff++)
0675         for(yoff=-1;yoff<2;yoff++) {
0676           if(xoff^^yoff) {
0677             switch(xoff) {
0678               case -1:
0679                 tx=Floor(x);
0680                 break;
0681               case 0:
0682                 tx=x;
0683                 break;
0684               case 1:
0685                 tx=Ceil(x);
0686                 break;
0687             }
0688             switch(yoff) {
0689               case -1:
0690                 ty=Floor(y);
0691                 break;
0692               case 0:
0693                 ty=y;
0694                 break;
0695               case 1:
0696                 ty=Ceil(y);
0697                 break;
0698             }
0699 //ONLY CHECK FOR WALL WE ARE WALKING TOWARDS
0700             if(xoff) {
0701               if(Sign(Cos(angle))!=xoff)
0702                 goto skip;
0703             }
0704             if(yoff) {
0705               if(Sign(Sin(angle))!=yoff)
0706                 goto skip;
0707             }
0708             if(HeightImpossible(w,x+xoff,y+yoff,height,TRUE,tallness)) {
0709 //.2 for stepping over partly open doors
0710               check:
0711               if(Abs(x-tx)<=radius&&xoff) {
0712                 if(adjust) {
0713                   x=tx+radius*1.05*-xoff;
0714                   hit=TRUE;
0715                 }
0716                 ret|=TRUE;
0717               }
0718               if(Abs(y-ty)<=radius&&yoff) {
0719                 if(adjust) {
0720                   y=ty+radius*1.05*-yoff;
0721                   hit=TRUE;
0722                 }
0723                 ret|=TRUE;
0724               }
0725             }
0726 skip:;
0727           }
0728         }
0729     }
0730   } while(hit);
0731   if(adjust) {
0732     *_x=x*GRID_SZ;
0733     *_y=y*GRID_SZ;
0734   }
0735   return ret;
0736 }
0737 Bool C3DWorldMoveZWithCollision(C3DWorld *w,F64 dist,F64 x,F64 y,F64 *_height,F64 tallness=.8) {
0738   F64 height=*_height;
0739   F64 z=height;
0740   F64 o=height;
0741   F64 by=dist/8;
0742   Bool hit=FALSE;
0743   F64 h_offset=0;
0744   F64 h,delta;
0745   I64 cnt=0,tile,best,ch,cr;
0746   F64 dir=Sign(dist);
0747   F64 oz=z;
0748 //Wut
0749   if(dir==0) return FALSE;
0750   for(cnt=0;Abs(dist)>1/256.;cnt++) {
0751     if(dir<0)
0752       delta=Max(dist,-1);
0753     else
0754       delta=Min(dist,1);
0755     dist-=delta;
0756     if(HeightImpossible(w,x/128,y/128,z+delta,TRUE,tallness)) {
0757       hit=TRUE;
0758       goto fix;
0759     }
0760     z+=delta;
0761   }
0762 fix:;
0763   while(HeightImpossible(w,x/GRID_SZ,y/GRID_SZ,z,TRUE,tallness)) {
0764     hit=TRUE;
0765     z-=dir/128.;
0766     if(dir>0&&z<oz) {
0767       z=oz;
0768       break;
0769     } if(dir<0&&z>oz) {
0770       z=oz;
0771       break;
0772     }
0773   }
0774 
0775   //Cant move anywhere but up
0776   if(z==oz) {
0777     while(HeightImpossible(w,x/GRID_SZ,y/GRID_SZ,z,TRUE,tallness))
0778       ++z;
0779   }
0780 
0781   if(hit)
0782     if(Abs(z-Floor(z))<=2*dir/128.)
0783       z=Floor(z);
0784   *_height=z;
0785   return hit;
0786 }
0787 //Returns true on wall hit
0788 Bool C3DWorldMoveWithCollision(C3DWorld *w,F64 angle,F64 dist,F64 *x,F64 *y,Bool adjust_smart=TRUE,F64 height=0) {
0789   F64 dist2=0,boundary;
0790   Bool hit_wall=FALSE,force;
0791   F64 old_x=w->x,old_y=w->y,deflect_angle;
0792   w->x=*x,w->y=*y;
0793   I64 at_tile;
0794   if(dist<0.) angle+=pi,dist=-dist;
0795   I64 idx,idx2;
0796   I64 h,ch,cr;
0797   if(RadiusHitsWall(w,angle,x,y,height,THING_RADIUS,TRUE,,RADF_CEIL|RADF_FLOOR|RADF_DOOR)) {
0798     hit_wall=TRUE;
0799     goto fin;
0800   }
0801   if(adjust_smart)
0802     while(dist2<dist) {
0803       for(idx=0;idx!=5;idx++) {
0804         dist2+=GRID_SZ/5.;
0805         if(dist2>=dist) dist2=dist;
0806         *x=w->x+Cos(angle)*dist2;
0807         *y=w->y+Sin(angle)*dist2;
0808         if(RadiusHitsWall(w,angle,x,y,height,THING_RADIUS,TRUE,,RADF_CEIL|RADF_FLOOR|RADF_DOOR)) {
0809           hit_wall=TRUE;
0810           goto fin;
0811         }
0812         if(HeightImpossible(w,*x/GRID_SZ,*y/GRID_SZ,height,TRUE,,.2)) {//.2 wiggle room for stepping over partly open doors
0813 //Totally reject move,something when wrong
0814           //     (would be placed on top of unreachable wall)
0815           *x=w->x;
0816           *y=w->y;
0817           hit_wall=TRUE;
0818           goto fin;
0819         }
0820         if(dist2>=dist)
0821           goto fin;
0822       }
0823     }
0824 fin:
0825   if(!(0<=w->x/128.<w->world_width)||
0826         !(0<=w->y/128.<w->world_height)
0827   ) {
0828     if(x)
0829       *x=Clamp(w->x/128.,0.,w->world_width)*128;
0830     if(y)
0831       *y=Clamp(w->y/128.,0.,w->world_height)*128;
0832     hit_wall=TRUE;
0833   }
0834   w->x=old_x,w->y=old_y;
0835   return hit_wall;
0836 }
0837 Bool AdjustRadiusThing(C3DWorld *w,C3DThing *self,C3DThing *other,F64 angle) {
0838   F64 sr=THING_RADIUS;
0839   F64 or=THING_RADIUS;
0840   F64 dist,push_back;
0841   I64 attempt=0;
0842   Bool ret=FALSE;
0843 again:
0844   dist=Sqrt(Sqr(other->x-self->x)+Sqr(other->y-self->y));
0845   if(dist<or+sr) {
0846     ret=TRUE;
0847     push_back=(sr+or-dist)*.1*1.4;
0848     C3DWorldMoveWithCollision(w,angle+pi,push_back,
0849           &self->x,
0850           &self->y,
0851           TRUE, //Slide along wall
0852           self->z);
0853     if(++attempt<15) goto again;
0854   }
0855   return ret;
0856 }
0857 
0858 Bool IsOnWaterTile(C3DThing *t) {
0859   return 5==BlockAtXYZ(t->x/GRID_SZ,t->y/GRID_SZ,Floor(t->z)-1);
0860 }
0861 Bool IsOnLavaTile(C3DThing *t) {
0862   return 10==BlockAtXYZ(t->x/GRID_SZ,t->y/GRID_SZ,Floor(t->z)-1);
0863 }
0864 CFrogNum *WorldManagerRemoveFromGrid(C3DWorldManager *wm,I64 *argv,I64 argc) {
0865   C3DThing *t=argv[0];
0866   if(!ThingHasClass(t,"C3DThing")) return FROG_SMALL_NIL;
0867   CFrogArray *arr=wm->grid;
0868   I64 atx=t->x/GRID_SZ/4.;
0869   I64 aty=t->y/GRID_SZ/4.;
0870   if(atx<0||aty<0) return FROG_SMALL_NIL;
0871   if(atx>=w->world_width/4+1) return FROG_SMALL_NIL;
0872   if(aty>=w->world_height/4+1) return FROG_SMALL_NIL;
0873   arr=arr->items->body[atx];
0874   arr=arr->items->body[aty];
0875   if(I64SetHas(arr->items,t)) {
0876     I64SetRem(arr->items,t);
0877   } else {
0878     for(atx=0;atx<w->world_width/4+1;atx++) {
0879       for(aty=0;aty<w->world_height/4+1;aty++) {
0880         arr=wm->grid;
0881         arr=arr->items->body[atx];
0882         arr=arr->items->body[aty];
0883         I64SetRem(arr->items,t);
0884       }
0885     }
0886   }
0887   return FROG_SMALL_NIL;
0888 }
0889 AddMethod("C3DWorldManager","removeFromGrid:",&WorldManagerRemoveFromGrid);
0890 CFrogNum *WorldManagerUpdateThingPos(C3DWorldManager *wm,I64 *argv,I64 argc) {
0891   C3DThing *t=argv[0];
0892   if(!ThingHasClass(t,"C3DThing")) return FROG_SMALL_NIL;
0893   CFrogArray *arr=wm->grid;
0894   I64 atx=t->x/GRID_SZ/4.;
0895   I64 aty=t->y/GRID_SZ/4.;
0896   if(atx<0||aty<0) return FROG_SMALL_NIL;
0897   if(atx>=w->world_width/4+1) return FROG_SMALL_NIL;
0898   if(aty>=w->world_height/4+1) return FROG_SMALL_NIL;
0899   arr=arr->items->body[atx];
0900   arr=arr->items->body[aty];
0901   if(!I64SetHas(arr->items,t))
0902     I64SetAdd(arr->items,t);
0903   return FROG_SMALL_NIL;
0904 }
0905 AddMethod("C3DWorldManager","updateThingPos:",&WorldManagerUpdateThingPos);
0906 CFrogArray *WorldManagerThingsInRadius(C3DWorldManager *wm,I64 *argv,I64 argc) {
0907   CFrogArray *arr=wm->grid,*ret=FrogArrayNew;
0908   C3DThing *t,**body;
0909   F64 x=AsF64(argv[0]),y=AsF64(argv[1]);
0910   I64 b_atx=x/GRID_SZ/4.,atx;
0911   I64 b_aty=y/GRID_SZ/4.,aty;
0912   I64 ox,oy,cnt;
0913   F64 r=AsF64(argv[2]);
0914   I64 ir=Ceil(r/128./4.+.001);
0915   for(ox=-ir;ox<=ir;ox++)
0916     for(oy=-ir;oy<=ir;oy++) {
0917       atx=ox+b_atx;
0918       aty=oy+b_aty;
0919       if(atx<0||aty<0) goto next;
0920       if(atx>=w->world_width/4+1) goto next;
0921       if(aty>=w->world_height/4+1) goto next;
0922       arr=wm->grid;
0923       arr=arr->items->body[atx];
0924       arr=arr->items->body[aty];
0925       cnt=arr->items->cnt;
0926       body=arr->items->body;
0927       while(--cnt>=0) {
0928         t=body[cnt];
0929         if(r*r>=Sqr(t->x-x)+Sqr(t->y-y)) {
0930           I64SetAdd(ret->items,t);
0931         }
0932       }
0933 next:;
0934     }
0935   return ret;
0936 }
0937 AddMethod("C3DWorldManager","getThingsInRadiusFromX:fromY:withRadius:",&WorldManagerThingsInRadius);
0938 U0 PhysicsOnThing(C3DWorld *w,C3DThing *t,C3DWorldManager *wm,F64 last_tS) {
0939   I64 wx=t->x/GRID_SZ,wy=t->y/GRID_SZ,idx;
0940   if(!(0<=wx<w->world_width))
0941     return;
0942   if(!(0<=wy<w->world_height))
0943     return;
0944 //I get about 20 fps on my aspire 5920(add 5 fps)
0945   F64 weight=1.;
0946   F64 angle,dist,ox,oy;
0947   I64 tile=wx+wy*w->world_width;
0948   Bool i_am_moving=FALSE,on_ground;
0949   CRocket *rocket=NULL;
0950   CFrogArray *in_radius;
0951   CDoor *to_open;
0952   C3DThing *other;
0953   CI64Set *set;
0954   if(ThingHasClass(t,"CRocket"))
0955     rocket=t;
0956   WorldManagerRemoveFromGrid(wm,&t,1);
0957   if(C3DWorldMoveZWithCollision(w,t->momz*weight+t->momz2*weight,t->x,t->y,&t->z)) {
0958     if(!rocket)  //Explode later(computer hitting floor after we move x/y)
0959       t->momz=0;
0960   } else if(!rocket&&!t->floating) {
0961     if(IsLiquidTile(BlockAtXYZ(t->x/128,t->y/128,t->z))) {
0962       t->momz=t->momz2-wm->gravity/3*weight;
0963     } else 
0964       t->momz-=wm->gravity*weight;
0965   }
0966   ox=t->x;
0967   oy=t->y;
0968   if(t->momy+t->momy2||t->momx+t->momx2) {
0969     if(!rocket) {
0970       t->momx*=29/30.*weight;
0971       t->momy*=29/30.*weight;
0972       if(Sign(t->momx)==-Sign(t->momx2))
0973         t->momx+=t->momx2*weight;
0974       if(Sign(t->momy)==-Sign(t->momy2))
0975         t->momy+=t->momy2*weight;
0976       if(Abs(t->momx)<2) t->momx=0;
0977       if(Abs(t->momy)<2) t->momy=0;
0978     }
0979 //Handle slow
0980     if(ThingHasClass(t,"CPlayer")) {
0981       t->friction=0.;
0982       if(IsOnWaterTile(t)) {
0983         t->friction=.25;
0984       } else if(IsOnLavaTile(t)) {
0985         t->friction=.5;
0986       } 
0987     }
0988     angle=Arg(t->momx+t->momx2,t->momy+t->momy2);
0989     i_am_moving=TRUE;
0990     dist=Sqrt(Sqr(t->momx+t->momx2)+Sqr(t->momy+t->momy2))*(1.-t->friction);
0991     on_ground=AsF64(CallScript("isOnGround",t));
0992     if(C3DWorldMoveWithCollision(w,angle,dist*weight,
0993           &t->x,
0994           &t->y,
0995           TRUE,
0996           t->z)) {
0997 
0998       hit:
0999       t->momx=0.;
1000       t->momy=0.;
1001       if(rocket) {
1002 explode:
1003         CallScript("explode",rocket);
1004         return;
1005       } else if(!ThingHasClass(t,"CPickup")) {
1006         to_open=CallScript("doorInRadius:atX:atY:atAngle:",
1007               wm,FrogNumNew(THING_RADIUS),FrogNumNew(t->x),FrogNumNew(t->y),FrogNumNew(t->angle));
1008         if(ThingHasClass(to_open,"CDoor")) {
1009           CallScript("activateBy:",to_open,t);
1010         }
1011       }
1012     }
1013     if(t->no_fall_off_edge&&on_ground) {
1014       if(AsF64(CallScript("isOverEdge",t))) {
1015         t->x=ox;
1016         t->y=oy;
1017         CallScript("update",t); //New Chase Dir
1018       }
1019     }
1020   } else if(RadiusHitsWall(w,angle,&t->x,&t->y,t->z,THING_RADIUS,TRUE,,RADF_CEIL|RADF_FLOOR|RADF_DOOR))
1021     goto hit;
1022   if(rocket) {
1023     if(!(0.<=t->x<w->world_width*128))
1024       goto explode;
1025     if(!(0.<=t->y<w->world_height*128))
1026       goto explode;
1027     if(AsF64(CallScript("checkExplode",rocket)))
1028       return;
1029   }
1030 //Only check for thing collision if we are the ones movin
1031   if(i_am_moving&&!rocket) {
1032     in_radius=CallScript("getObstaclesInRadiusForThing:inRadius:",wm,t,FrogNumNew(2*THING_RADIUS));
1033     set=in_radius->items;
1034     for(idx=0;idx!=set->cnt;idx++) {
1035       other=set->body[idx];
1036       if(other!=t&&Max(other->z,t->z)<=Min(other->z+THING_HEIGHT,t->z+THING_HEIGHT))
1037         if(AdjustRadiusThing(w,t,other,angle))
1038           t->hit_thing=TRUE;
1039     }  
1040   }
1041   if(rocket) {
1042     if(HeightImpossible(w,t->x/GRID_SZ,t->y/GRID_SZ,t->z,TRUE,1.))
1043       goto explode;
1044   }
1045   WorldManagerUpdateThingPos(wm,&t,1);
1046 }
1047 F64 Lerp(F64 per,F64 min,F64 max) {
1048   if(min<max)
1049     return Clamp(per*(max-min)+min,min,max);
1050   return Clamp(per*(max-min)+min,max,min);
1051 }
1052 
1053 //-1 terminated
1054 //https://github.com/cgyurgyik/fast-voxel-traversal-algorithm/blob/master/overview/FastVoxelTraversalOverview.md
1055 I64 *GetTilesInPath(C3DWorld *w,F64 angle,F64 dist) {
1056   CI64Set *t=I64SetNew;
1057   F64 x=w->x/128,y=w->y/128,slope,cos,sin;
1058   I64 tx=x/GRID_SZ,ty=y/GRID_SZ,idx;
1059   I64 cnt=dist/128.;  
1060   Bool x_type;
1061   cos=Cos(angle);
1062   sin=Sin(angle);
1063   I64 stepx=Sign(cos);
1064   I64 stepy=Sign(sin);
1065   F64 current_x_index=Max(1,Ceil(x));
1066   F64 current_y_index=Max(1,Ceil(y));
1067   if(cos<0) current_x_index--;
1068   if(sin<0) current_y_index--;
1069   F64 tMaxX=(current_x_index-x)/cos;
1070   F64 tMaxY=(current_y_index-y)/sin;
1071   tx=x;
1072   ty=y;
1073   idx=ty*w->world_width+tx;
1074   I64SetAdd(t,idx);
1075   while(--cnt>=0) {
1076     if(tMaxX<tMaxY) {
1077       tMaxX+=1./Abs(cos);
1078       tx+=stepx;
1079     } else {
1080       tMaxY+=1./Abs(sin);
1081       ty+=stepy;
1082     } 
1083 
1084     if(0<=tx<w->world_width)
1085       if(0<=ty<w->world_height) {
1086         idx=ty*w->world_width+tx;
1087         I64SetAdd(t,idx);
1088         goto pass;
1089       }
1090     break;
1091 pass:;
1092   }
1093 fin:
1094   I64SetAdd(t,-1);
1095   I64 *tiles=t->body;
1096   Free(t);
1097   return tiles;
1098 }
1099 
1100 extern CDC *WhichTexture(C3DWorld *w,I64 tile,F64 h,F64 h2,F64 *hit_h,Bool *hit_wall);
1101 CDC *ThingFace(C3DWorld *w,C3DThing *thing,Bool *flip=NULL) {
1102   C3DThingTemplate *template=thing->template;
1103   if(template==FROG_SMALL_NIL) return NULL;
1104   F64 off=thing->angle-w->angle+pi,gap;
1105   I64 face=1;
1106   off=off%(2*pi);
1107   CDC *use_dc=NULL;
1108   I64 frame;
1109   F64 diff;
1110   for(gap=0.;gap<2*pi;gap+=2.*pi/4.) {
1111     diff=(off-gap+3*pi)%(2*pi)-pi;
1112 //https://stackoverflow.com/questions/12234574/calculating-if-an-angle-is-between-two-angles
1113     if(-2*pi/4./2<=diff<=2*pi/4./2)
1114       break;
1115     face++;
1116   }
1117   if(flip) *flip=FALSE;
1118   switch(face) {
1119     default:
1120     case 1:
1121       use_dc=template->front;
1122       break;
1123     case 2:
1124       use_dc=template->side;
1125       if(flip) *flip=TRUE;
1126       break;
1127     case 3:
1128       use_dc=template->back;
1129       break;
1130     case 4:
1131       use_dc=template->side;
1132       break;
1133   }
1134 draw:
1135   if(!ThingHasClass(use_dc,"CFrogImg")) return NULL;
1136   use_dc=use_dc(CFrogImg*)->dc;
1137   if(!use_dc||!AnimDCCnt(use_dc)) return NULL;
1138   frame=ToI64((Frog_tS-thing->animation_start_tS)/ANIM_DELAY);
1139   frame=MaxI64(frame,0); //Prevent poo poo suace
1140   if(thing->animation_no_repeat) 
1141     use_dc+=MinI64(frame,AnimDCCnt(use_dc)-1);
1142   else
1143     use_dc+=frame%AnimDCCnt(use_dc);
1144   return use_dc;
1145 }
1146 
1147 
1148 Bool PointToScrn(C3DWorld *w,F64 x,F64 y,F64 z,CD3 *p) {
1149   z-=w->cam_height;
1150   y=(y-w->y)/128;
1151   x=(x-w->x)/128;
1152   F64 o=Sqrt(z*z+x*x+y*y);
1153   CD3 view_plane;
1154   CD3 pt1;
1155   pt1.z=z;
1156   pt1.x=x*Cos(-w->angle)-y*Sin(-w->angle);
1157   pt1.y=x*Sin(-w->angle)+y*Cos(-w->angle);
1158   x=pt1.x;
1159   y=pt1.y;
1160   pt1.x=x*Cos(w->angle2)-z*Sin(w->angle2);
1161   pt1.z=x*Sin(w->angle2)+z*Cos(w->angle2);
1162   x=pt1.x;
1163   z=pt1.z;
1164 
1165   p->x=x;
1166   p->y=y;
1167   p->z=z;
1168 
1169   //Begind camera
1170   if(p->x<=1e-2) {
1171     p->x=1e-2;
1172     D3MulEqu(p,1./p->x);
1173     D3Equ(p,
1174           (p->y/2+.5)*GR_WIDTH,
1175           (p->z/-2+.5)*GR_HEIGHT,
1176           o*128
1177           );
1178     return FALSE;
1179   } else {
1180     D3MulEqu(p,1./p->x);
1181     D3Equ(p,
1182           (p->y/2+.5)*GR_WIDTH,
1183           (p->z/-2+.5)*GR_HEIGHT,
1184           o*128
1185           );
1186     return TRUE;
1187   }
1188 }
1189 
1190 
1191 U0 Mat4x4MulXYZ_F64(F64 *r,F64 *_x,F64 *_y,F64 *_z)
1192 {
1193   F64 x1,y1,z1,xx=*_x,yy=*_y,zz=*_z;
1194   x1=(r[0*4+0]*xx+r[0*4+1]*yy+r[0*4+2]*zz+r[0*4+3]);
1195   y1=(r[1*4+0]*xx+r[1*4+1]*yy+r[1*4+2]*zz+r[1*4+3]);
1196   z1=(r[2*4+0]*xx+r[2*4+1]*yy+r[2*4+2]*zz+r[2*4+3]);
1197   *_x=x1;*_y=y1;*_z=z1;
1198 }
1199 
1200 
1201 U0 PointsToMat(CD3 *corners,F64 *mat) {
1202 //https://math.stackexchange.com/questions/186286/get-transformation-matrix-from-points
1203   F64 x1=corners[0].x;
1204   F64 x2=corners[1].x;
1205   F64 x3=corners[2].x;
1206   F64 x4=corners[3].x;
1207 
1208   F64 y1=corners[0].y;
1209   F64 y2=corners[1].y;
1210   F64 y3=corners[2].y;
1211   F64 y4=corners[3].y;
1212 
1213   F64 i=1;
1214 
1215 
1216   F64 j = (y1 - y2 + y3 - y4) / (y2 - y3);
1217   F64 k = (x1 - x2 + x3 - x4) / (x4 - x3);
1218   F64 m = (y4 - y3) / (y2 - y3);
1219   F64 n = (x2 - x3) / (x4 - x3);
1220 
1221   F64 h,g;
1222   if((1-m*n)==0.||y2==y3||x4==x3)
1223     h=0,g=0;
1224   else {
1225     h = i * (j - k * m) / (1 - m * n);
1226     g = i * (k - j * n) / (1 - m * n);
1227   }
1228   F64 c = x1 * i;
1229   F64 f = y1 * i;
1230   F64 a = x4 * (g + i) - x1 * i;
1231   F64 b = x2 * (h + i) - x1 * i;
1232   F64 d = y4 * (g + i) - y1 * i;
1233   F64 e = y2 * (h + i) - y1 * i;
1234 
1235   mat[0]=a;
1236   mat[1]=b;
1237   mat[2]=c;
1238   mat[3]=d;
1239   mat[4]=e;
1240   mat[5]=f;
1241   mat[6]=g;
1242   mat[7]=h;
1243   mat[8]=i;
1244 
1245 }
1246 U0 Mat3x3Inv(F64 *matrix) {
1247   F64 a=matrix[0];
1248   F64 b=matrix[1];
1249   F64 c=matrix[2];
1250   F64 d=matrix[3];
1251   F64 e=matrix[4];
1252   F64 f=matrix[5];
1253   F64 g=matrix[6];
1254   F64 h=matrix[7];
1255   F64 i=matrix[8];
1256 //https://stackoverflow.com/questions/63981471/how-do-i-solve-inverse-of-3x3-matrices-without-using-a-library
1257   F64 xx=e*i-h*f;
1258   F64 yy=f*g-d*i;
1259   F64 zz=d*h-g*e;
1260   F64 det=a*xx+b*yy+c*zz;
1261   matrix[0]=xx/det;
1262   matrix[1]=(c*h-b*i)/det;
1263   matrix[2]=(b*f-c*e)/det;
1264   matrix[3]=yy/det;
1265   matrix[4]=(a*i-c*g)/det;
1266   matrix[5]=(d*c-a*f)/det;
1267   matrix[6]=zz/det;
1268   matrix[7]=(g*b-a*h)/det;
1269   matrix[8]=(a*e-d*b)/det;
1270 }
1271 
1272 Bool DrawQuad(C3DWorld *w,CDC *texture,CD3 *points,Bool record=FALSE) {
1273   CD3 screen[4],dummya,dummyb,dummyc;
1274   I64 i,x,y;
1275   I64 idist;
1276   I32 *depth_b;
1277   F64 mat[9];
1278   F64 tx,ty,tz,dist;
1279   F64 lxt,lyt,hxt,hyt; //Texture cordnates
1280   CDC *dc=w->to_dc;
1281   U8 *dst;
1282   Bool hit=FALSE;
1283   Bool found=FALSE,failed=FALSE;
1284   I64 failed2=0;
1285   I64 force_color=0;
1286   F64 farest=0;
1287   for(i=0;i!=4;i++) {
1288     hit=PointToScrn(w,points[i].x,points[i].y,points[i].z,&screen[i]);
1289     found|=hit;
1290     failed|=!hit;
1291     if(!hit) {
1292       Bts(&failed2,i);
1293     }
1294     dist=Sqrt(
1295           Sqr(points[i].x-w->x)+
1296           Sqr(points[i].y-w->y)+
1297           Sqr((points[i].z-w->cam_height)*128));
1298     if(dist>farest)
1299       farest=dist;
1300   }
1301   if(!found) return FALSE;
1302   hit=FALSE;
1303   PointsToMat(screen,mat);  
1304   Mat3x3Inv(mat);
1305   I64 lx=screen[0].x,hx=screen[0].x;
1306   I64 ly=screen[0].y,hy=screen[0].y;
1307   for(i=1;i!=4;i++) {
1308     lx=MinI64(lx,screen[i].x);
1309     ly=MinI64(ly,screen[i].y);
1310     hx=MaxI64(hx,screen[i].x);
1311     hy=MaxI64(hy,screen[i].y);
1312   }
1313   lx--;
1314   ly--;
1315   hx++;
1316   hy++;
1317   lx&=~3;
1318   ly&=~3;
1319   if(hy&3) hy=hy&~3+4;
1320   if(hx&3) hx=hx&~3+4;
1321   lx=ClampI64(lx,0,GR_WIDTH-4);
1322   ly=ClampI64(ly,0,GR_HEIGHT-4);
1323   hy=ClampI64(hy,0,GR_HEIGHT-4);
1324   hx=ClampI64(hx,0,GR_WIDTH-4);
1325   idist=ToI64(farest);
1326   idist|=idist<<32;
1327   for(x=lx;x<hx;x+=4) {
1328     dst=&dc->body[x+ly*dc->width_internal];
1329     depth_b=&dc->depth_buf[x+ly*dc->width_internal];;
1330     for(y=ly;y<hy;y+=4) {
1331       if(depth_b[0]>farest) {
1332         tx=mat[0]*x+mat[1]*y+mat[2];
1333         ty=mat[3]*x+mat[4]*y+mat[5];
1334         tz=mat[6]*x+mat[7]*y+mat[8];
1335         tx/=tz,ty/=tz;
1336         i=GrPeek(texture,
1337               tx*texture->width,
1338               ty*texture->height
1339               );
1340         if(0<=i<=15) {
1341           i=gr.to_8_colors[i];
1342           if(force_color) i=force_color;
1343           dst[0](U32)=i;
1344           depth_b[0](U64)=idist;
1345           depth_b[2](U64)=idist;
1346           dst+=dc->width_internal;
1347           depth_b+=dc->width_internal;
1348 
1349           dst[0](U32)=i;
1350           depth_b[0](U64)=idist;
1351           depth_b[2](U64)=idist;
1352           dst+=dc->width_internal;
1353           depth_b+=dc->width_internal;
1354 
1355           dst[0](U32)=i;
1356           depth_b[0](U64)=idist;
1357           depth_b[2](U64)=idist;
1358           dst+=dc->width_internal;
1359           depth_b+=dc->width_internal;
1360 
1361           dst[0](U32)=i;
1362           depth_b[0](U64)=idist;
1363           depth_b[2](U64)=idist;
1364           dst+=dc->width_internal;
1365           depth_b+=dc->width_internal;
1366 
1367           if(GR_HEIGHT/2/4==y/4)
1368             if(GR_WIDTH/2/4==x/4) {
1369               hit=TRUE;
1370             }
1371         } else {
1372 skip:;
1373           dst+=dc->width_internal*4;
1374           depth_b+=dc->width_internal*4;
1375         }
1376       } else
1377         goto skip;
1378     }
1379   }
1380   return hit;
1381 }
1382 
1383 
1384 Bool IsLeftSide(CD2 *a,CD2 *b,CD2 *c) {
1385   return (b->x-a->x)*(c->y-a->y)-(b->y-a->y)*(c->x-a->x)>=0.;
1386 }
1387 
1388 U0 DrawBlock(C3DWorldManager *man,I64 x,I64 y,I64 z) {
1389   C3DWorld *w=man->world_handle;
1390   I64 b=BlockAtXYZ(x,y,z),s;
1391   CDC *t;
1392   if(b==-1||!b) 
1393     return ;
1394   CD3 points[4];
1395   x*=128;
1396   y*=128;
1397   if(t=w->floor_textures[b]) {
1398     points[0].x=x;
1399     points[0].y=y;
1400     points[0].z=z;
1401     points[1].x=x+128;
1402     points[1].y=y;
1403     points[1].z=z;
1404     points[2].x=x+128;
1405     points[2].y=y+128;
1406     points[2].z=z;
1407     points[3].x=x;
1408     points[3].y=y+128;
1409     points[3].z=z;
1410     if(!BlockAtXYZ(x/128,y/128,z+1))
1411       if(DrawQuad(w,t,&points)) {
1412         man->cur_tile_x=x/128;
1413         man->cur_tile_y=y/128;
1414         man->cur_tile_z=z;
1415         man->cur_tile_side_x=0;
1416         man->cur_tile_side_y=0;
1417         man->cur_tile_side_z=1;
1418       }
1419   }
1420   if(t=w->ceil_textures[b]) {
1421     points[0].x=x;
1422     points[0].y=y;
1423     points[0].z=z-1;
1424     points[1].x=x+128;
1425     points[1].y=y;
1426     points[1].z=z-1;
1427     points[2].x=x+128;
1428     points[2].y=y+128;
1429     points[2].z=z-1;
1430     points[3].x=x;
1431     points[3].y=y+128;
1432     points[3].z=z-1;
1433     if(!BlockAtXYZ(x/128,y/128,z-1))
1434       if(DrawQuad(w,t,&points)) {
1435         man->cur_tile_x=x/128;
1436         man->cur_tile_y=y/128;
1437         man->cur_tile_z=z;
1438         man->cur_tile_side_x=0;
1439         man->cur_tile_side_y=0;
1440         man->cur_tile_side_z=-1;
1441       }
1442   }
1443   if(t=w->wall_textures[b]) {
1444     points[0].z=z;
1445     points[1].z=z;
1446     points[2].z=z-1;
1447     points[3].z=z-1;
1448 
1449     points[0].x=x;
1450     points[0].y=y;
1451     points[1].x=x+128;
1452     points[1].y=y;
1453     points[2].x=x+128;
1454     points[2].y=y;
1455     points[3].x=x;
1456     points[3].y=y;
1457     if(!IsLeftSide(&points[0],&points[1],&w->x))
1458       if(!BlockAtXYZ(x/128,y/128-1,z))
1459         if(DrawQuad(w,t,&points)) {
1460           man->cur_tile_x=x/128;
1461           man->cur_tile_y=y/128;
1462           man->cur_tile_z=z;
1463           man->cur_tile_side_x=0;
1464           man->cur_tile_side_y=-1;
1465           man->cur_tile_side_z=0;
1466         }
1467     points[0].x=x;
1468     points[0].y=y;
1469     points[1].x=x;
1470     points[1].y=y+128;
1471     points[2].x=x;
1472     points[2].y=y+128;
1473     points[3].x=x;
1474     points[3].y=y;
1475     if(!IsLeftSide(&points[1],&points[0],&w->x))
1476       if(!BlockAtXYZ(x/128-1,y/128,z))
1477         if(DrawQuad(w,t,&points)) {
1478           man->cur_tile_x=x/128;
1479           man->cur_tile_y=y/128;
1480           man->cur_tile_z=z;
1481           man->cur_tile_side_x=-1;
1482           man->cur_tile_side_y=0;
1483           man->cur_tile_side_z=0;
1484         }
1485     points[0].x=x+128;
1486     points[0].y=y+128;
1487     points[1].x=x;
1488     points[1].y=y+128;
1489     points[2].x=x;
1490     points[2].y=y+128;
1491     points[3].x=x+128;
1492     points[3].y=y+128;
1493     if(!IsLeftSide(&points[0],&points[1],&w->x))
1494       if(!BlockAtXYZ(x/128,y/128+1,z))
1495         if(DrawQuad(w,t,&points)) {
1496           man->cur_tile_x=x/128;
1497           man->cur_tile_y=y/128;
1498           man->cur_tile_z=z;
1499           man->cur_tile_side_x=0;
1500           man->cur_tile_side_y=1;
1501           man->cur_tile_side_z=0;
1502         }
1503 
1504     points[0].x=x+128;
1505     points[0].y=y+128;
1506     points[1].x=x+128;
1507     points[1].y=y;
1508     points[2].x=x+128;
1509     points[2].y=y;
1510     points[3].x=x+128;
1511     points[3].y=y+128;
1512     if(!IsLeftSide(&points[1],&points[0],&w->x))
1513       if(!BlockAtXYZ(x/128+1,y/128,z))
1514         if(DrawQuad(w,t,&points)) {
1515           man->cur_tile_x=x/128;
1516           man->cur_tile_y=y/128;
1517           man->cur_tile_z=z;
1518           man->cur_tile_side_x=1;
1519           man->cur_tile_side_y=0;
1520           man->cur_tile_side_z=0;
1521         }
1522   }
1523 }
1524 
1525 F64 NormalizeAngle(F64 a) {
1526   a%=2*pi;
1527   if(a<0) a+=2*pi;
1528   return a;
1529 }
1530 
1531 
1532 Bool ClipLineToCamera(C3DWorld *w,CD3 *a,CD3 *b) {
1533   CD2 cam,left,right;
1534   CD2 dst;
1535   CD3 oa,ob;
1536   F64 a_angle=Arg(a->x-w->x,a->y-w->y);
1537   F64 b_angle=Arg(b->x-w->x,b->y-w->y);
1538   F64 dist=Sqrt(Sqr(b->x-a->x)+Sqr(b->y-a->y));
1539   Bool in_view=FALSE;
1540   oa.x=a->x;
1541   oa.y=a->y;
1542   oa.z=a->z;
1543   ob.x=b->x;
1544   ob.y=b->y;
1545   ob.z=b->z;
1546   cam.x=w->x;
1547   cam.y=w->y;
1548   left.x=cam.x+I16_MAX*Cos(pi/2.5+w->angle);
1549   left.y=cam.y+I16_MAX*Sin(pi/2.5+w->angle);
1550   right.x=cam.x+I16_MAX*Cos(-pi/2.5+w->angle);
1551   right.y=cam.y+I16_MAX*Sin(-pi/2.5+w->angle);
1552   if(PlaneIntersect(&dst,a,b,&cam,&left)) {
1553     if(IsLeftSide(&cam,&left,a)) {
1554       a->z=Lerp(Sqrt(Sqr(dst.x-oa.x)+Sqr(dst.y-oa.y))/dist,oa.z,ob.z);
1555       a->x=dst.x;
1556       a->y=dst.y;
1557     }
1558     if(IsLeftSide(&cam,&left,b)){
1559       b->x=dst.x;
1560       b->y=dst.y;
1561       b->z=Lerp(Sqrt(Sqr(dst.x-oa.x)+Sqr(dst.y-oa.y))/dist,oa.z,ob.z);
1562     }
1563     in_view=TRUE;
1564   }
1565   if(PlaneIntersect(&dst,a,b,&cam,&right)) {
1566     if(!IsLeftSide(&cam,&right,a)) {
1567       a->x=dst.x;
1568       a->y=dst.y;
1569       a->z=Lerp(Sqrt(Sqr(dst.x-oa.x)+Sqr(dst.y-oa.y))/dist,oa.z,ob.z);
1570     }
1571     if(!IsLeftSide(&cam,&right,b)){
1572       b->x=dst.x;
1573       b->y=dst.y;
1574       b->z=Lerp(Sqrt(Sqr(dst.x-oa.x)+Sqr(dst.y-oa.y))/dist,oa.z,ob.z);
1575     }
1576     in_view=TRUE;
1577   }
1578   if(in_view) return TRUE;
1579   if(Cos(a_angle-w->angle)<0&&Cos(b_angle-w->angle)<0)
1580     return FALSE;
1581   return TRUE;
1582 //  return in_view||!IsLeftSide(&cam,&left,a)||!IsLeftSide(&cam,&left,b)
1583   //    ||IsLeftSide(&cam,&right,a)||IsLeftSide(&cam,&right,b);
1584 }
1585 
1586 
1587 //Also prepares CWallStain's
1588 U0 PrepareThingsForDraw(C3DWorldManager *wm,F64 thing_scale=.6) {
1589   I64 args[3];
1590   args[0]=FrogNumNew(w->x),args[1]=FrogNumNew(w->y),args[2]=FrogNumNew(VIEW_DIST*128);
1591   CFrogArray *arr=WorldManagerThingsInRadius(wm,args,3);
1592   C3DThing **things=arr->items->body,*thing;
1593   CWallStain **stains,*stain;
1594   I64 cnt=arr->items->cnt,ix,iy,ox,oy,idx;
1595   Bool visible;
1596   F64 normal;
1597   CDC *hit_texture,*to_dc=w->to_dc;
1598   CD3 scrn_st,scrn_en;
1599   CD3 scrn_corners[4];
1600   I64 mnx,mxx,mny,mxy;
1601   CD3 clip_st,clip_en;
1602   U32 *dst_ptr;
1603   U64 *dst_db_ptr;
1604   F64 xx,yy,matrix[9],zz,matrix2[9];
1605   F64 width;
1606   F64 st,en;
1607   F64 stz,enz;
1608   U32 dist;
1609   w->things_around_camera_origin_x=Floor(w->x/128.)-VIEW_DIST;
1610   w->things_around_camera_origin_y=Floor(w->y/128.)-VIEW_DIST;
1611   while(--cnt>=0) {
1612     thing=things[cnt];
1613     if(thing->no_draw)
1614       goto skip;
1615     hit_texture=ThingFace(wm->world_handle,thing,&thing->draw_flip);
1616     if(!hit_texture)
1617       goto skip;
1618     thing->draw_face=hit_texture;
1619     normal=Arg(w->x-thing->x,w->y-thing->y)-pi/2;
1620     scrn_st.x=thing->x-hit_texture->width/2.*Cos(normal)*thing_scale;
1621     scrn_st.y=thing->y-hit_texture->width/2.*Sin(normal)*thing_scale;
1622     scrn_en.x=scrn_st.x+hit_texture->width*Cos(normal)*thing_scale;
1623     scrn_en.y=scrn_st.y+hit_texture->width*Sin(normal)*thing_scale;
1624     scrn_st.z=0;
1625     scrn_en.z=0;
1626 
1627     D3Copy(&clip_st,&scrn_st);
1628     D3Copy(&clip_en,&scrn_en);
1629     if(!ClipLineToCamera(w,&clip_st,&clip_en)) {
1630       goto skip;
1631     }
1632 
1633     visible=FALSE;
1634     visible|=PointToScrn(w,clip_st.x,clip_st.y,thing->z+hit_texture->height/128.*thing_scale,&scrn_corners[0]);
1635     visible|=PointToScrn(w,clip_st.x,clip_st.y,thing->z,&scrn_corners[1]);
1636     visible|=PointToScrn(w,clip_en.x,clip_en.y,thing->z,&scrn_corners[2]);
1637     visible|=PointToScrn(w,clip_en.x,clip_en.y,thing->z+hit_texture->height/128.*thing_scale,&scrn_corners[3]);
1638     if(visible) {
1639       st=0;
1640       en=1.;
1641 
1642       //If we clipped the line,compute the new start and end
1643       st=Sqrt(Sqr(scrn_st.y-clip_st.y)+Sqr(scrn_st.x-clip_st.x))/(hit_texture->width*thing_scale);
1644       en=Sqrt(Sqr(scrn_st.y-clip_en.y)+Sqr(scrn_st.x-clip_en.x))/(hit_texture->width*thing_scale);
1645 
1646       stz=Sqrt(Sqr(w->x-clip_st.x)+Sqr(w->y-clip_st.y));
1647       enz=Sqrt(Sqr(w->x-clip_en.x)+Sqr(w->y-clip_en.y));
1648 
1649       PointsToMat(scrn_corners,matrix);      
1650       MemCpy(matrix2,matrix,8*9);
1651       Mat3x3Inv(matrix);
1652       mnx=GR_WIDTH;
1653       mxx=0;
1654       mny=GR_HEIGHT;
1655       mxy=0;
1656       for(idx=0;idx!=4;idx++) {
1657         mnx=MinI64(mnx,scrn_corners[idx].x);
1658         mxx=MaxI64(mxx,scrn_corners[idx].x);
1659         mny=MinI64(mny,scrn_corners[idx].y);
1660         mxy=MaxI64(mxy,scrn_corners[idx].y);
1661       }
1662       mnx=ClampI64(mnx,0,GR_WIDTH)&~3;
1663       mxx=ClampI64(mxx,0,GR_WIDTH)&~3;
1664       mny=ClampI64(mny,0,GR_HEIGHT)&~3;
1665       mxy=ClampI64(mxy,0,GR_HEIGHT)&~3;
1666       for(ix=mnx;ix<mxx;ix+=4) {
1667         dst_ptr=&to_dc->body[ix+mny*to_dc->width_internal];
1668         dst_db_ptr=&to_dc->depth_buf[ix+mny*to_dc->width_internal];
1669         for(iy=mny;iy<mxy;iy+=4) {
1670           xx=matrix[0]*ix+matrix[1]*iy+matrix[2];
1671           yy=matrix[3+0]*ix+matrix[3+1]*iy+matrix[3+2];
1672           zz=matrix[6+0]*ix+matrix[6+1]*iy+matrix[6+2];
1673           xx/=zz;
1674           yy/=zz;
1675           if(0.<=xx<1.)
1676             if(0.<=yy<1.) {
1677 //Account for "clipped" start/end
1678               if(thing->draw_flip) {
1679                 xx=Lerp(xx,1.-st,1.-en);
1680                 dist=Lerp(xx,enz,stz);
1681               } else {
1682                 xx=Lerp(xx,st,en);
1683                 dist=Lerp(xx,stz,enz);
1684               }
1685               to_dc->color=GrPeek(hit_texture,xx*(hit_texture->width-1),(hit_texture->height-1)*yy);
1686               if(TRANSPARENT>to_dc->color>=0) {
1687                 for(idx=0;idx!=4;idx++) {
1688                   if(dst_db_ptr->i32[0]>dist) {
1689                     *dst_ptr=gr.to_8_colors[to_dc->color];
1690                     *dst_db_ptr=dist|dist<<32;
1691                     dst_db_ptr[1]=dist|dist<<32;
1692                   }
1693                   dst_ptr(U8*)+=to_dc->width_internal;
1694                   dst_db_ptr(I32*)+=to_dc->width_internal;
1695                 }
1696 
1697                 goto pass;
1698               }
1699             }
1700           dst_ptr(U8*)+=4*to_dc->width_internal;
1701           dst_db_ptr(I32*)+=4*to_dc->width_internal;
1702 pass:;
1703         }
1704       }
1705     }
1706 skip:;
1707   }
1708 
1709   //Stains
1710   for(ox=0;ox!=2*VIEW_DIST;ox++)
1711     for(oy=0;oy!=2*VIEW_DIST;oy++) {
1712       if(!w->stains_around_camera[ox][oy])
1713         w->stains_around_camera[ox][oy]=I64SetNew;
1714       else
1715         w->stains_around_camera[ox][oy]->cnt=0;
1716     }
1717 
1718   stains=wm->wall_stains->items->body;
1719   cnt=wm->wall_stains->items->cnt;
1720   while(--cnt>=0) {
1721     stain=stains[cnt];
1722     ix=stain->tile_x-w->things_around_camera_origin_x;
1723     iy=stain->tile_y-w->things_around_camera_origin_y;
1724     if(0<=ix<2*VIEW_DIST)
1725       if(0<=iy<2*VIEW_DIST)
1726         I64SetAdd(w->stains_around_camera[ix][iy],stain);
1727   }
1728 }
1729 I64 ToTile(I64 block) {
1730 return block/256;
1731 }
1732 
1733 //https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection
1734 #ifdef TARGET_X86
1735 Bool SegmentSegmentIntersect(CD2 *hit_at,CD2 *a,CD2 *b,CD2 *a2,CD2 *b2,F64 *t,F64 *u) {
1736   CD2 _a,_b,_a2,_b2,tmpb,tmpt,tmpu;
1737   _a.x=a->x;
1738   _a.y=a->y;
1739   _b.x=b->x;
1740   _b.y=b->y;
1741   _a2.x=a2->x;
1742   _a2.y=a2->y;
1743   _b2.x=b2->x;
1744   _b2.y=b2->y;
1745   MOVUPD XMM3,&_a[RBP];
1746   MOVUPD XMM4,&_b[RBP];
1747   MOVUPD XMM5,&_a2[RBP];
1748   MOVUPD XMM1,&_b2[RBP];
1749 
1750 
1751   MOVUPD XMM0,XMM3;
1752   SUBPD XMM0,XMM4 //a-b
1753   MOVUPD XMM2,XMM5
1754   SUBPD XMM2,XMM1 //a2-b2
1755 //xx
1756   MOVSD2 &tmpb+8[RBP],XMM0 
1757   MOVSD2 &tmpb[RBP],XMM2
1758 //yy  
1759   SHUFPD XMM0,XMM2,0b11
1760   MOVUPD XMM2,&tmpb[RBP]
1761   MULPD XMM2,XMM0
1762   MOVUPD &tmpb[RBP],XMM2
1763 
1764   MOVUPD XMM2,XMM5
1765   SUBPD XMM2,XMM1 //a2-b2
1766   MOVUPD XMM0,XMM3;
1767   SUBPD XMM0,XMM5 //a-a2
1768 //xx 
1769   MOVSD2 &tmpt+8[RBP],XMM0 
1770   MOVSD2 &tmpt[RBP],XMM2 
1771   SHUFPD XMM0,XMM2,0b11 //Move yy into XMMM0
1772   MOVUPD XMM2,&tmpt[RBP]
1773   MULPD XMM2,XMM0
1774   MOVUPD &tmpt[RBP],XMM2
1775 
1776 
1777   MOVUPD XMM2,XMM3;
1778   SUBPD XMM2,XMM5 //a-a2
1779   MOVUPD XMM0,XMM3;
1780   SUBPD XMM0,XMM4 //a-b
1781 //xx 
1782   MOVSD2 &tmpu+8[RBP],XMM0 
1783   MOVSD2 &tmpu[RBP],XMM2 
1784   SHUFPD XMM0,XMM2,0b11 //Move yy into XMMM0
1785   MOVUPD XMM2,&tmpu[RBP]
1786   MULPD XMM2,XMM0
1787   MOVUPD &tmpu[RBP],XMM2
1788 
1789   //Do these after the asm blob to avoid messing up tmp registers
1790   F64 bottom=tmpb.y-tmpb.x;
1791   F64 _t=(tmpt.y-tmpt.x)/bottom;
1792   F64 _u=-(tmpu.y-tmpu.x)/bottom;
1793 
1794   if(0<=_t<=1)
1795     if(0<=_u<=1) {
1796       if(t) *t=_t;
1797       if(u) *u=_u;
1798       if(hit_at) {
1799         hit_at->x=a->x+_t*(b->x-a->x);
1800         hit_at->y=a->y+_t*(b->y-a->y);
1801       }
1802       return TRUE;
1803     }
1804   return FALSE;
1805 }
1806 #else
1807 Bool SegmentSegmentIntersect(CD2 *hit_at,CD2 *a,CD2 *b,CD2 *a2,CD2 *b2,F64 *t,F64 *u) {
1808   F64 bottom=(a->x-b->x)*(a2->y-b2->y)-(a->y-b->y)*(a2->x-b2->x);
1809   F64 _t=((a->x-a2->x)*(a2->y-b2->y)-(a->y-a2->y)*(a2->x-b2->x))/bottom;
1810   F64 _u=-((a->x-b->x)*(a->y-a2->y)-(a->y-b->y)*(a->x-a2->x))/bottom;
1811   if(0<=_t<=1)
1812     if(0<=_u<=1) {
1813       if(t) *t=_t;
1814       if(u) *u=_u;
1815       if(hit_at) {
1816         hit_at->x=a->x+_t*(b->x-a->x);
1817         hit_at->y=a->y+_t*(b->y-a->y);
1818       }
1819       return TRUE;
1820     }
1821   return FALSE;
1822 }
1823 #endif
1824 //https://github.com/cgyurgyik/fast-voxel-traversal-algorithm/blob/master/overview/FastVoxelTraversalOverview.md
1825 #define RAY_TRACE_MAX_DEPTH 3
1826 I64 BlockHitColor(CD3 *at,CD3 *ray,I64 x,I64 y,I64 z,I64 sidex,I64 sidey,I64 sidez,CD3 *hit_at=NULL,F64 *hit_d=NULL,CD2I64 *texture_cords=NULL) {
1827   CD3 pos,dst;
1828   CDC *texture=NULL;
1829   I64 b=BlockAtXYZ(x,y,z),tx,ty;
1830   pos.x=x;
1831   pos.y=y;
1832   pos.z=z;
1833   if(sidex>0) {
1834     pos.x=x+1;
1835   }
1836   else if(sidex<0) {
1837     pos.x=x;
1838   }
1839 
1840   else if(sidey>0) {
1841     pos.y=y+1;
1842   }
1843   else if(sidey<0) {
1844     pos.y=y;
1845   }
1846   else if(sidez>0) {
1847     pos.z=z+1;
1848   }
1849   else if(sidez<0) {
1850     pos.z=z;
1851   } else
1852     return LTPURPLE;
1853   F64 d;
1854   if(sidez>0) {
1855     texture=w->floor_textures[b];
1856 zstyle:
1857     d=(pos.z-at->z)/ray->z;
1858     D3Mul(&dst,d,ray);
1859     D3AddEqu(&dst,at);
1860     tx=dst.x*128;
1861     ty=dst.y*128;
1862     ty=ty&127;
1863   } else if(sidez<0) {
1864     texture=w->ceil_textures[b];
1865     goto zstyle;
1866   } else if(sidex) {
1867     texture=w->wall_textures[b];
1868     d=(pos.x-at->x)/ray->x;
1869     D3Mul(&dst,d,ray);
1870     D3AddEqu(&dst,at);
1871     tx=dst.y*128;
1872     ty=dst.z*128;
1873     ty=127-ty&127;
1874   } else if(sidey) {
1875     texture=w->wall_textures[b];
1876     d=(pos.y-at->y)/ray->y;
1877     D3Mul(&dst,d,ray);
1878     D3AddEqu(&dst,at);
1879     tx=dst.x*128;
1880     ty=dst.z*128;
1881     ty=127-ty&127;
1882   }
1883   if(hit_d) 
1884     *hit_d=Sqrt(Sqr(dst.z-at->z)+Sqr(dst.y-at->y)+Sqr(dst.x-at->x));
1885   if(hit_at)
1886     D3Equ(hit_at,dst.x*128,dst.y*128,dst.z);
1887   if(texture_cords) {
1888     texture_cords->x=tx&127;
1889     if(sidez)
1890       texture_cords->y=ty&127;
1891     else
1892       texture_cords->y=127-ty;
1893   }
1894   if(texture)
1895     return GrPeek(texture,tx&127,ty&127);
1896   return BLACK;
1897 }
1898 I64 GetColorForRay(C3DWorldManager *man,CD3 *origin,CD3 *ray,F64 dist,F64 *hit_at,CD3 *hit_cords,I64 depth=0,I64 last_block=0,I64 fog_color=BLACK,Bool record_tile=FALSE) { 
1899   CFrogArray *grid=man->grid;
1900   C3DWorld *w=man->world_handle;
1901   CI64Set *do_things;
1902   F64 x=origin->x/128,y=origin->y/128;
1903   CDoor *door;
1904   Bool hit=FALSE;
1905   F64 hit_dist=1e100;
1906   CWallStain **wall_stains,*wall_stain;
1907   I64 wall_stain_cnt;
1908   I64 hit_color=-1;
1909   I64 tx=x,ty=y,idx,tz;
1910   I64 cnt=dist/128.;  
1911   I64 stepx=Sign(ray->z);
1912   I64 stepy=Sign(-ray->x);
1913   I64 stepz=Sign(-ray->y);
1914   if(-.001<=Ceil(x)-x<=.001) x-=.001;
1915   if(-.001<=Ceil(y)-y<=.001) y-=.001;
1916   if(-.001<=Ceil(origin->z)-origin->z<=.001) origin->z-=.001;
1917   F64 current_x_index=Ceil(x);
1918   F64 current_y_index=Ceil(y);
1919   F64 current_z_index=Ceil(origin->z);
1920   if(stepx<0) current_x_index--;
1921   if(stepy<0) current_y_index--;
1922   if(stepz<0) current_z_index--;
1923   F64 tMaxX=(current_x_index-x)/ray->z;
1924   F64 tMaxY=(current_y_index-y)/-ray->x;
1925   F64 tMaxZ=(current_z_index-origin->z)/-ray->y;
1926   F64 h,h2;
1927   F64 angle=Arg(ray->z,-ray->x);
1928   F64 cos=Cos(angle),sin=Sin(angle);
1929   F64 slope=-ray->y/Sqrt(Sqr(ray->z)+Sqr(ray->x)); //???
1930   F64 r_abs_rx=1./Abs(ray->x);
1931   F64 r_abs_ry=1./Abs(ray->y);
1932   F64 r_abs_rz=1./Abs(ray->z);
1933   CDC *hit_texture;
1934   CDC *stain_dc;
1935   I64 cur,thing_idx,texture_x,texture_y;
1936   I64 wall_side;
1937   I64 stain_x,stain_y;
1938   I64 color;
1939   I64 relx,rely;
1940   F64 lighting;
1941   CD2 long,long128,xyhit,origin_d_128;
1942   I64 side;
1943   CD2I64 texture_cords;
1944   I64 sidex=0,sidey=0,sidez=0;
1945   CD3 o128,rp;
1946   rp.x=ray->z;
1947   rp.y=-ray->x;
1948   rp.z=-ray->y;
1949   o128.x=origin->x/128;
1950   o128.y=origin->y/128;
1951   o128.z=origin->z;
1952   long.x=x+VIEW_DIST*cos;
1953   long.y=y+VIEW_DIST*sin;
1954   long128.x=long.x*128;
1955   long128.y=long.y*128;
1956   tx=x;
1957   ty=y;
1958   tz=Floor(origin->z);
1959   for(cur=0;cur!=cnt&&!hit;cur++) {
1960     if(I8_MIN<=tz<I8_MAX)
1961       if(0<=tx<w->world_width)
1962         if(0<=ty<w->world_height) {
1963           idx=ty*w->world_width+tx;
1964           lighting=ClampI64(w->world_lighting[idx*256+tz-I8_MIN],0,15);
1965           relx=tx-w->things_around_camera_origin_x;
1966           rely=ty-w->things_around_camera_origin_y;
1967           idx=(ty*w->world_width+tx)*256+tz-I8_MIN;
1968           if(w->world_blocks[idx]) {
1969             if(w->world_blocks[idx]==last_block) goto pass;
1970 tile:;
1971             CD3 new_origin;
1972             CD3 new_ray;
1973             idx=(ty*w->world_width+tx)*256+tz-I8_MIN;
1974             idx=w->world_blocks[idx];
1975             if(record_tile) {
1976               man->cur_tile_x=tx;
1977               man->cur_tile_y=ty;
1978               man->cur_tile_z=tz;
1979               man->cur_tile_side_x=sidex;
1980               man->cur_tile_side_y=sidey;
1981               man->cur_tile_side_z=sidez;
1982             }
1983             color=BlockHitColor(&o128,&rp,tx,ty,tz,sidex,sidey,sidez,&new_origin,&hit_dist,&texture_cords);
1984             if(0<=relx<2*VIEW_DIST)
1985               if(0<=rely<2*VIEW_DIST)
1986                 if(cnt=(do_things=w->stains_around_camera[relx][rely])->cnt)
1987                   goto try_stains;
1988             goto try_block;
1989 try_stains:;
1990             wall_stains=do_things->body;
1991             wall_stain_cnt=cnt;
1992             while(--wall_stain_cnt>=0) {
1993               wall_stain=wall_stains[wall_stain_cnt];
1994               if((sidex&&wall_stain->sidex==sidex)||(sidey&&wall_stain->sidey==sidey)||(sidez&&wall_stain->sidez==sidez))
1995                 if(wall_stain->tile_x==tx&&wall_stain->tile_y==ty&&wall_stain->tile_z==tz) {
1996                   if(0<=GrPeek(wall_stain->dc,
1997                         (texture_cords.x&127),
1998                         (texture_cords.y&127)
1999                         )<TRANSPARENT) {
2000                     color=GrPeek(wall_stain->dc,texture_cords.x&127,texture_cords.y&127);
2001                     goto valid_color;
2002                   }
2003 
2004                 }
2005             }
2006 try_block:;
2007 //For-recursive
2008             if(0&&IsLiquidTile(idx)&&depth<RAY_TRACE_MAX_DEPTH) {
2009 //TODO displace ray based on "wavew"
2010               fog_color=color&0xff;
2011               depth++;
2012               last_block=idx;
2013 //Weird issues  with exact values
2014               //Snells law
2015               F64 snell_a,snell_a2,normal;
2016               F64 t=Frog_tS/30.;
2017               F64 rand_refrac=1.1+.1*(Abs(Sin((t/4.)*200))-Abs(Cos(t*125)))+.1*(Rand-.5)*2;
2018               if(!sidez) {
2019                 normal=(1+side)*pi/2;
2020                 snell_a=Arg(origin->x-xyhit.x*128,origin->y-xyhit.y*128)-normal;
2021                 snell_a=ASin(Sin(snell_a)/rand_refrac);
2022                 snell_a=normal-snell_a+pi;
2023                 new_ray.x=Sin(snell_a);
2024                 new_ray.y=ray->y*ASin(Sin((pi/2-ATan(slope)))/(rand_refrac+.2));
2025                 new_ray.z=-Cos(snell_a);
2026               } else {
2027                 if(slope<0.)
2028                   normal=pi/2;
2029                 else
2030                   normal=-pi/2;
2031                 snell_a=ATan(slope)-normal;
2032                 snell_a=ASin(Sin(snell_a)/rand_refrac);
2033                 snell_a=normal-snell_a+pi;
2034                 new_ray.x=ray->x;
2035                 new_ray.y=-Sin(snell_a);
2036                 new_ray.z=ray->z;
2037               }
2038               return GetColorForRay(man,&new_origin,&new_ray,128*VIEW_DIST,hit_at,hit_cords,depth,last_block,fog_color);
2039             }
2040 valid_color:
2041             if(hit_cords) {
2042               D3Copy(hit_cords,&new_origin);
2043             }
2044             hit_dist*=128;
2045             if(hit_at) *hit_at=hit_dist;
2046             hit=TRUE;
2047             F64 ao_nodes[4];
2048             F64 adj_lights[4];
2049             F64 ao;
2050             for(idx=0;idx!=4;idx++)
2051               ao_nodes[idx]=BlockAO(idx,tx,ty,tz,sidex,sidey,sidez);
2052             for(idx=0;idx!=4;idx++)
2053               adj_lights[idx]=CornerLight(idx,tx,ty,tz,sidex,sidey,sidez);
2054             F64 u0=texture_cords.x/127.;
2055             F64 u1=1-texture_cords.x/127.;
2056             F64 v0=texture_cords.y/127.;
2057             F64 v1=1-texture_cords.y/127.;
2058 //Bilnear
2059             ao=.5;
2060             ao+=ao_nodes[3] *v1*u1;
2061             ao+=ao_nodes[2] *u0*v1;
2062             ao+=ao_nodes[0] *u1*v0;
2063             ao+=ao_nodes[1] *u0*v0;
2064             lighting=LightAtXYZ(tx+sidex,ty+sidey,tz+sidez);
2065             lighting-=3-ao;
2066             lighting=(Lerp(u0,adj_lights[0],adj_lights[2])+Lerp(v0,adj_lights[1],adj_lights[3])+lighting)/3.;
2067             hit_color=Fog(color,hit_dist+1+depth*FOG_DIST*4+MaxI64((15-lighting)*FOG_DIST,0),fog_color);
2068           }
2069           goto pass;
2070         }
2071     break;
2072 pass:;
2073     sidex=sidey=sidez=0;
2074     if(tMaxX<tMaxY) {
2075       if(tMaxX<tMaxZ) {
2076         tMaxX+=r_abs_rz;
2077         tx+=stepx;
2078         sidex=-stepx;
2079       } else {
2080         tMaxZ+=r_abs_ry;
2081         tz+=stepz;
2082         sidez=-stepz;
2083       }
2084     } else {
2085       if(tMaxY<tMaxZ) {
2086         tMaxY+=r_abs_rx;
2087         ty+=stepy;
2088         sidey=-stepy;
2089       } else {
2090         tMaxZ+=r_abs_ry;
2091         tz+=stepz;
2092         sidez=-stepz;
2093       }
2094     } 
2095   }
2096 fin:
2097   if(depth&&!hit) {
2098     return gr.to_8_colors[fog_color&0xff];
2099   }
2100   return hit_color;
2101 }
2102 
2103 I64 mp_done=0;
2104 
2105 //https://blog.scottlogic.com/2020/03/10/raytracer-how-to.html
2106 //ca==Cos(-w->angle)
2107 //sa==Sin(-w->angle)
2108 //ca2==Cos(-w->angle2)
2109 //sa2==Sin(-w->angle2)
2110 U0 GenerateRay(C3DWorld *world,CD2 *ray_look,F64 i,F64 j,F64 width,F64 height,F64 ca,F64 sa,F64 ca2,F64 sa2) {
2111   CD3 m,tmpy,tmpx;
2112   D3Equ(&m,(i/width-.5)*-2,(j/height-.5)*2,1);
2113   D3Equ(&tmpy,
2114         m.x,
2115         m.y*ca2-m.z*sa2,
2116         m.y*sa2+m.z*ca2
2117         );
2118   D3Equ(ray_look,
2119         tmpy.x*ca+tmpy.z*sa,
2120         tmpy.y,
2121         -tmpy.x*sa+tmpy.z*ca,
2122         );
2123   D3Norm(ray_look);
2124 }
2125 
2126 
2127 U0 MPCastCol(C3DWorldManager *wm) {
2128   C3DWorld *w=wm->world_handle;
2129   F64 angle=w->angle-w->fov/2,angle2,hit_dist,ath;
2130   F64 dist,dist2,h,h2;
2131   F64 sin,width=GR_WIDTH,cos1,sin1,tan1,cos12;
2132   I64 cap,idx,x,y;
2133   CDoor *door;
2134   CDC *to_dc=w->to_dc;
2135   CDC *what;
2136   CD3 *p;
2137   I64 *tiles,tile;
2138   I64 color,col;
2139   U32 *ptr;
2140   I32 *depth_ptr;
2141   I64 tx,ty;
2142   I64 segment;
2143   I64 idist,row;
2144   Bool hit_wall;
2145   Bool record_tile=FALSE;
2146   I64 fogc;
2147   CD3 ray;
2148   F64 angle_offset_table[GR_WIDTH];
2149   F64 y_angle_offset_table[GR_HEIGHT];
2150   F64 xrot=w->angle2;
2151   F64 sa=Sin(-w->angle);
2152   F64 ca=Cos(-w->angle);
2153   F64 sa2=Sin(-w->angle2);
2154   F64 ca2=Cos(-w->angle2);
2155   Bool in_water=FALSE;
2156   F64 t=Frog_tS/30.,rand_refrac,snell_a,snell_a2;
2157   if(IsLiquidTile(BlockAtXYZ(w->x/128.,w->y/128.,Floor(w->cam_height)))) {
2158     in_water=TRUE;
2159   }
2160   idx=ToF64(Gs->num)/ToF64(mp_cnt)*GR_WIDTH;
2161   cap=ToF64(Gs->num+1)/ToF64(mp_cnt)*GR_WIDTH;
2162   for(x=idx;x<cap;x+=w->step_width) {
2163 // divide by cos2_lookup_table to get 32 blocks from the camera
2164     <1>
; 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 ptr=&to_dc->body[x]; 2185 depth_ptr=&to_dc->depth_buf[x]; 2186 for(y=0;y<GR_HEIGHT;y+=w->step_width) { 2187 //Nroot has no idea what he was doing here.Literaly typing random stuff 2188 GenerateRay(w,&ray,x,y,GR_WIDTH,GR_HEIGHT,ca,sa,ca2,sa2); 2189 if(in_water) { 2190 rand_refrac=1.1+.1*(Abs(Sin((t/4.)*200))-Abs(Cos(t*125)))+.1*(Rand-.5)*2; 2191 snell_a2=(y/ToF64(GR_HEIGHT)-.5)*w->fov; 2192 snell_a=(x/ToF64(GR_WIDTH)-.5)*w->fov; 2193 snell_a=ASin(Sin(snell_a)/rand_refrac)/2.; 2194 snell_a2=ASin(Sin(snell_a2)/(rand_refrac+.2))/2.; 2195 D3Equ(&ray, 2196 ray.x*Cos(snell_a)+ray.z*Sin(snell_a), 2197 ray.y, 2198 -ray.x*Sin(snell_a)+ray.z*Cos(snell_a), 2199 ); 2200 D3Equ(&ray, 2201 ray.x, 2202 ray.y*Cos(snell_a2)-ray.z*Sin(snell_a2), 2203 ray.y*Sin(snell_a2)+ray.z*Cos(snell_a2) 2204 ); 2205 //TODO what kind of liquid are we in TOODO 2206 fogc=LTBLUE; 2207 } else 2208 fogc=BLACK; 2209 p=&w->to_point[x/4][y/4]; 2210 //If we dont hit a wall,just fill with "far out in the distacnve" 2211 p->x=w->x+ray.x*I16_MAX; 2212 p->y=w->y+ray.y*I16_MAX; 2213 p->z=w->cam_height; 2214 CD3 origin; 2215 origin.x=w->x; 2216 origin.y=w->y; 2217 origin.z=w->cam_height; 2218 //depth==1 to simulate going through a (liquid) block 2219 record_tile=FALSE; 2220 if(x/w->step_width==(GR_WIDTH/2/w->step_width)) 2221 if(y/w->step_width==(GR_HEIGHT/2/w->step_width)) { 2222 record_tile=TRUE; 2223 } 2224 2225 if(in_water) { 2226 color=GetColorForRay(wm,&origin,&ray,32*GRID_SZ,&dist,p,1*in_water,BlockAtXYZ(w->x/128.,w->y/128.,Floor(w->cam_height)),fogc,record_tile); 2227 } else 2228 color=GetColorForRay(wm,&origin,&ray,32*GRID_SZ,&dist,p,,,,record_tile); 2229 idist=dist; 2230 idist|=idist<<32; 2231 if(depth_ptr[0]<dist) { 2232 ptr(U8*)+=to_dc->width_internal*w->step_width; 2233 depth_ptr(I32*)+=to_dc->width_internal*w->step_width; 2234 goto fin; 2235 } 2236 if(color!=-1&&w->step_width==2) { 2237 for(col=0;col!=2;col++) { 2238 if(col&1) 2239 ptr(U16*)[0]=color; 2240 else 2241 ptr(U16*)[0]=color>>8; //Dither 2242 depth_ptr[0](I64)=idist; 2243 ptr(U8*)+=to_dc->width_internal; 2244 depth_ptr+=to_dc->width_internal; 2245 } 2246 goto fin; 2247 } 2248 if(color!=-1&&w->step_width==4) { 2249 for(col=0;col!=4;col++) { 2250 if(col&1) 2251 ptr(U32*)[0]=color; 2252 else 2253 ptr(U32*)[0]=color>>8; //Dither 2254 depth_ptr[0](I64)=idist; 2255 depth_ptr[2](I64)=idist; 2256 ptr(U8*)+=to_dc->width_internal; 2257 depth_ptr+=to_dc->width_internal; 2258 } 2259 goto fin; 2260 } 2261 if(color!=-1&&w->step_width==6) { 2262 for(col=0;col!=6;col++) { 2263 if(col&1) { 2264 ptr(U32*)[0]=color; 2265 ptr(U16*)[2]=color; 2266 } else { 2267 ptr(U32*)[0]=color>>8; //Dither 2268 ptr(U16*)[2]=color>>8; //Dither 2269 } 2270 depth_ptr[0](I64)=idist; 2271 depth_ptr[2](I64)=idist; 2272 depth_ptr[4](I64)=idist; 2273 ptr(U8*)+=to_dc->width_internal; 2274 depth_ptr+=to_dc->width_internal; 2275 } 2276 goto fin; 2277 } 2278 if(color!=-1&&w->step_width==8) { 2279 for(col=0;col!=8;col++) { 2280 if(col&1) 2281 ptr(U64*)[0]=color; 2282 else 2283 ptr(U64*)[0]=color>>8|color<<(64-8); //Rotate for Dither 2284 depth_ptr[0](I64)=idist; 2285 depth_ptr[2](I64)=idist; 2286 depth_ptr[4](I64)=idist; 2287 depth_ptr[6](I64)=idist; 2288 ptr(U8*)+=to_dc->width_internal; 2289 depth_ptr+=to_dc->width_internal; 2290 } 2291 goto fin; 2292 } 2293 ptr(U8*)+=to_dc->width_internal*w->step_width; 2294 depth_ptr(I32*)+=to_dc->width_internal*w->step_width; 2295 fin:; 2296 } 2297 } 2298 LBts(&mp_done,Gs->num); 2299 } 2300 U0 C3DWorldCastRays(C3DWorld *w) { 2301 I64 c; 2302 C3DWorldManager *man=FrogSymbol("world")->value; 2303 man->cur_tile_x=-1; 2304 man->cur_tile_y=-1; 2305 man->cur_tile_z=-1; 2306 lock mp_done=0; 2307 /* for(c=1;c<mp_cnt;++c) { 2308 JobQue(&MPCastCol,man,c); 2309 } 2310 MPCastCol(man); 2311 for(c=1;c<mp_cnt;++c) { 2312 while(!Bt(&mp_done,c)) 2313 __Sleep(2); 2314 }*/ 2315 I64 x=w->x/128.,y=w->y/128.,z=w->cam_height; 2316 I64 ox,oy,oz; 2317 for(oz=-16;oz<=16;oz++) { 2318 for(ox=-16;ox<=16;ox++) 2319 for(oy=-16;oy<=16;oy++) 2320 DrawBlock(man,x+ox,y+oy,z+oz); 2321 } 2322 } 2323 Bool CanSee2DThing(C3DWorld *w,C3DThing *t) { 2324 Bool ret=TRUE; 2325 F64 dist=Sqrt(Sqr(t->x-w->x)+Sqr(t->y-w->y)),near,far,interp; 2326 F64 angle=Arg(t->x-w->x,t->y-w->y); 2327 I64 *ti=GetTilesInPath(w,angle,dist),idx; 2328 I64 at_tile=ToI64(w->x/GRID_SZ)+ToI64(w->y/GRID_SZ)*w->world_width; 2329 I64 t_at_tile=ToI64(t->x/GRID_SZ)+ToI64(t->y/GRID_SZ)*w->world_width; 2330 I64 x,y,z; 2331 I64 cnt=0; 2332 for(idx=0;ti[idx]!=-1;idx++) 2333 ++cnt; 2334 for(idx=0;idx<cnt;idx++) { 2335 x=ti[idx]%w->world_width; 2336 y=ti[idx]/w->world_width; 2337 interp=Lerp(ToF64(idx)/ToF64(cnt),w->cam_height,t->z); 2338 if(BlockAtXYZ(x,y,ToI64(interp))) { 2339 //Check if we are 1 block within result "good enough" 2340 if(Abs(x-t->x/GRID_SZ)<0||Abs(y-t->y/GRID_SZ)<0 2341 ||Abs(z-t->z)<0) 2342 break; //ret==TRUE 2343 ret=FALSE; 2344 break; 2345 } 2346 } 2347 Free(ti); 2348 return ret; 2349 } 2350 #define THING_DRAW_CUTOFF GRID_SZ/2 2351 2352 2353 U0 WMPointToScreenX(C3DWorldManager *man,I64 *argv,I64 argc) { 2354 CD3 p; 2355 PointToScrn(w, 2356 AsF64(argv[0]), 2357 AsF64(argv[1]), 2358 AsF64(argv[2]), 2359 &p); 2360 return FrogNumNew(p.x); 2361 } 2362 U0 WMPointToScreenY(C3DWorldManager *man,I64 *argv,I64 argc) { 2363 CD3 p; 2364 PointToScrn(w, 2365 AsF64(argv[0]), 2366 AsF64(argv[1]), 2367 AsF64(argv[2]), 2368 &p); 2369 return FrogNumNew(p.y); 2370 } 2371 U0 WMPointToScreenZ(C3DWorldManager *man,I64 *argv,I64 argc) { 2372 CD3 p; 2373 PointToScrn(w, 2374 AsF64(argv[0]), 2375 AsF64(argv[1]), 2376 AsF64(argv[2]), 2377 &p); 2378 return FrogNumNew(p.z); 2379 } 2380 AddMethod("C3DWorldManager","pointToScreenXAtX:atY:atZ:",&WMPointToScreenX); 2381 AddMethod("C3DWorldManager","pointToScreenYAtX:atY:atZ:",&WMPointToScreenY); 2382 AddMethod("C3DWorldManager","pointToScreenZAtX:atY:atZ:",&WMPointToScreenZ); 2383 U0 DrawParticle(C3DWorld *w,C3DParticle *p) { 2384 I32 dist=Sqrt(Sqr(w->x-p->x)+Sqr(w->y-p->y)); 2385 I64 x0,y0; 2386 CD3 res; 2387 w->to_dc->color=p->color; 2388 w->to_dc->thick=(DIST_SCALE/ToF64(1+dist))/GRID_SZ*p->size; 2389 PointToScrn(w,p->x,p->y,p->z,&res); 2390 if(w->to_dc->thick>0) 2391 GrPlot3(w->to_dc,res.x,res.y,dist+1); 2392 } 2393 2394 U0 Line3D(C3DWorld *w,CDC *dc,CD3 *st,CD3 *en) { 2395 //Make sure the line isnt insanely cose the the camera. 2396 //I approximate the things based on angles,its pretty dumb. 2397 CD3 clip,clip2,res,res2; 2398 F64 angle=Arg(en->x-st->x,en->y-st->y); 2399 clip.x=st->x; 2400 clip.y=st->y; 2401 clip.z=st->z; 2402 clip2.x=en->x; 2403 clip2.y=en->y; 2404 clip2.z=en->z; 2405 2406 ClipLineToCamera(w,&clip,&clip2); 2407 2408 2409 if(1.>Sqr(clip.x-w->x)+Sqr(clip.y-w->y)) { 2410 clip.x=32*Cos(angle)+w->x; 2411 clip.y=32*Sin(angle)+w->y; 2412 } 2413 if(1.>Sqr(clip2.x-w->x)+Sqr(clip2.y-w->y)) { 2414 clip2.x=32*Cos(angle)+w->x; 2415 clip2.y=32*Sin(angle)+w->y; 2416 } 2417 2418 PointToScrn(w,clip.x,clip.y,clip.z,&res); 2419 PointToScrn(w,clip2.x,clip2.y,clip2.z,&res2); 2420 GrLine3(dc,res.x,res.y,res.z,res2.x,res2.y,res2.z); 2421 } 2422 2423 U0 WallStainDel(CWallStain *s) { 2424 if(s->dc) AnimDCDel(s->dc); 2425 Free(s); 2426 } 2427 // 2428 // SmallTalk Section 2429 // 2430 U0 FloorStainDel(CFloorStain *s) { 2431 if(s->dc) AnimDCDel(s->dc); 2432 Free(s); 2433 } 2434 2435 CFrogThing *WallStainSetGr(CWallStain *self,I64 *argv,I64 argc) { 2436 I64 x=AsF64(argv[1]),y=AsF64(argv[2]); 2437 CFrogImg *img=argv[0]; 2438 CDC *loaded; 2439 CFrogNum *ret=FrogNumNew(0); 2440 if(ThingHasClass(img,"CFrogImg")) { 2441 if(!self->dc) { 2442 self->dc=DCNew(128,128,frog_mem_task); 2443 DCFill(self->dc); 2444 } 2445 loaded=img->dc; 2446 ret=FrogNumNew(GrBlot(self->dc,x-loaded->width/2,y-loaded->height/2,loaded)); 2447 } 2448 return ret; 2449 } 2450 AddMethod("CWallStain","addGraphics:atX:atY:",&WallStainSetGr); 2451 CFrogThing *WorldManagerNewParticle(C3DWorldManager *man,I64 *argv,I64 argc) { 2452 F64 x=AsF64(argv[0]); 2453 F64 y=AsF64(argv[1]); 2454 F64 z=AsF64(argv[2])+.001; //For not hiting floor at spawn 2455 F64 spread=AsF64(argv[3]); 2456 F64 color=AsF64(argv[4]); 2457 F64 a=Rand*2*pi; 2458 NewParticle(w,color,16,x,y,z,spread*Cos(a),spread*Sin(a),man->gravity*3); 2459 return FROG_SMALL_NIL; 2460 } 2461 2462 AddMethod("C3DWorldManager","basicNewParticleAtX:atY:atZ:withSpread:withColor:",&WorldManagerNewParticle); 2463 CFrogThing *WorldManagerNewParticle2(C3DWorldManager *man,I64 *argv,I64 argc) { 2464 F64 x=AsF64(argv[0]); 2465 F64 y=AsF64(argv[1]); 2466 F64 z=AsF64(argv[2])+.001; //For not hiting floor at spawn 2467 F64 spread=AsF64(argv[3]); 2468 F64 color=AsF64(argv[4]); 2469 U8 *splat_lump=AsString(argv[5]); 2470 F64 a=Rand*2*pi; 2471 NewParticle(w,color,16,x,y,z,spread*Cos(a),spread*Sin(a),man->gravity*3,splat_lump); 2472 Free(splat_lump); 2473 return FROG_SMALL_NIL; 2474 } 2475 2476 AddMethod("C3DWorldManager","basicNewParticleAtX:atY:atZ:withSpread:withColor:withSplatLump:",&WorldManagerNewParticle2); 2477 CFrogThing *WorldManagerNewSweepSound(C3DWorldManager *man,I64 *argv,I64 argc) { 2478 if(man->sound_task) Kill(man->sound_task,FALSE); 2479 man->sound_task=Sweep( 2480 AsF64(argv[0]), 2481 AsF64(argv[1]), 2482 AsF64(argv[2])); 2483 return FROG_SMALL_NIL; 2484 } 2485 AddMethod("C3DWorldManager","sweepSoundForTime:withMin:withMax:",&WorldManagerNewSweepSound); 2486 CFrogThing *WorldManagerNewNoiseSound(C3DWorldManager *man,I64 *argv,I64 argc) { 2487 if(man->sound_task) Kill(man->sound_task,FALSE); 2488 man->sound_task=Noise( 2489 AsF64(argv[0]), 2490 AsF64(argv[1]), 2491 AsF64(argv[2])); 2492 return FROG_SMALL_NIL; 2493 } 2494 AddMethod("C3DWorldManager","noiseSoundForTime:withMin:withMax:",&WorldManagerNewNoiseSound); 2495 CFrogThing *WorldManagerGetQuestChatBot(C3DWorldManager *man,I64 *argv,I64 argc) { 2496 U8 *name=AsString(argv[0]); 2497 CEliza *e=FROG_SMALL_NIL; 2498 if(name) 2499 e=LoadQuestChatBot(name); 2500 Free(name); 2501 return e; 2502 } 2503 AddMethod("C3DWorldManager","getQuestChatBot:",&WorldManagerGetQuestChatBot); 2504 CFrogNum *WorldManagerIsLiquid(C3DWorldManager *man,I64 *argv,I64 argc) { 2505 I64 x=AsF64(argv[0]); 2506 I64 y=AsF64(argv[1]); 2507 I64 z=Floor(AsF64(argv[2])); 2508 I64 tile=(x+y*w->world_width)*256+z-I8_MIN; 2509 if(0<=tile<w->world_width*w->world_height) { 2510 return FrogNumNew(IsLiquidTile(w->world_blocks[tile])); 2511 } 2512 return FrogNumNew(0); 2513 } 2514 AddMethod("C3DWorldManager","tileIsLiquidAtX:atY:atZ:",&WorldManagerIsLiquid); 2515 2516 //Returns 1. if COLLISION,else 0 2517 CFrogThing *C3DThingMoveAtAngle(C3DThing *self,I64 *argv,I64 argc) { 2518 CFrogThing *dist=argv[0]; 2519 CFrogThing *angle=argv[1]; 2520 Bool hit_wall=FALSE; 2521 // 2522 // 2523 //Heres the deal,world update gets ran 10 fps,but physics is 30 fps 2524 //I will "dumb" walk into a wall then set the momentum to "walk" 2525 // 2526 // 2527 F64 old_x=self->x; 2528 F64 old_y=self->y; 2529 hit_wall=C3DWorldMoveWithCollision(w,AsF64(angle),AsF64(dist),&self->x,&self->y,TRUE,self->z); 2530 self->x=old_x; 2531 self->y=old_y; 2532 self->momx2+=AsF64(dist)*Cos(AsF64(angle)); 2533 self->momy2+=AsF64(dist)*Sin(AsF64(angle)); 2534 return FrogNumNew(hit_wall); 2535 } 2536 AddMethod("C3DThing","move:atAngle:",&C3DThingMoveAtAngle); 2537 2538 CFrogThing *WorldManagerWidth(C3DWorldManager *self,I64 *argv,I64 argc) { 2539 return FrogNumNew(w->world_width); 2540 } 2541 CFrogThing *WorldManagerHeight(C3DWorldManager *self,I64 *argv,I64 argc) { 2542 return FrogNumNew(w->world_height); 2543 } 2544 2545 AddMethod("C3DWorldManager","width",&WorldManagerWidth); 2546 AddMethod("C3DWorldManager","height",&WorldManagerHeight); 2547 CFrogThing *WorldManagerSetTileAtXAtY(C3DWorldManager *self,I64 *argv,I64 argc) { 2548 I8 tile=AsF64(argv[0]); 2549 I64 x=AsF64(argv[1]); 2550 I64 y=AsF64(argv[2]); 2551 I64 z=AsF64(argv[3]); 2552 if(0<=x<w->world_width) 2553 if(0<=y<w->world_height) { 2554 if(I8_MIN<=z<I8_MAX) 2555 return w->world_blocks[(x+y*w->world_width)*256+z-I8_MIN]=tile; 2556 } 2557 return FROG_SMALL_NIL; 2558 } 2559 AddMethod("C3DWorldManager","setTile:atX:atY:atZ:",&WorldManagerSetTileAtXAtY); 2560 2561 2562 CFrogThing *ThingCanSeeThing(C3DThing *self,I64 *argv,I64 argc) { 2563 F64 old_x=w->x,old_y=w->y,old_z=w->cam_height; 2564 CFrogThing *ret; 2565 C3DThing *other=argv[0]; 2566 if(!ThingHasClass(other,"C3DThing")) 2567 return FROG_SMALL_NIL; 2568 w->x=self->x; 2569 w->y=self->y; 2570 //TODO replace "+1" with thing height 2571 w->cam_height=self->z+1.; 2572 ret=FrogNumNew(CanSee2DThing(w,other)); 2573 w->x=old_x; 2574 w->y=old_y; 2575 w->cam_height=old_z; 2576 return ret; 2577 } 2578 AddMethod("C3DThing","canSeeThing:",&ThingCanSeeThing); 2579 2580 2581 CFrogThing *ScreenCoordToDist(C3DWorldManager *self,I64 *argv,I64 argc) { 2582 CD3 *p=&w->to_point[GR_WIDTH/2/4][GR_HEIGHT/2/4]; 2583 return FrogNumNew(Sqrt(Sqr(p->x-w->x)+Sqr(p->y-w->y))); 2584 } 2585 AddMethod("C3DWorldManager","screenCoordToDist",&ScreenCoordToDist); 2586 CFrogThing *ScreenCoordToX(C3DWorldManager *self,I64 *argv,I64 argc) { 2587 CD3 *p=&w->to_point[GR_WIDTH/2/4][GR_HEIGHT/2/4]; 2588 return FrogNumNew(p->x); 2589 } 2590 AddMethod("C3DWorldManager","screenCoordToX",&ScreenCoordToX); 2591 CFrogThing *ScreenCoordToY(C3DWorldManager *self,I64 *argv,I64 argc) { 2592 CD3 *p=&w->to_point[GR_WIDTH/2/4][GR_HEIGHT/2/4]; 2593 return FrogNumNew(p->y); 2594 } 2595 AddMethod("C3DWorldManager","screenCoordToY",&ScreenCoordToY); 2596 CFrogThing *ScreenCoordToZ(C3DWorldManager *self,I64 *argv,I64 argc) { 2597 CD3 *p=&w->to_point[GR_WIDTH/2/4][GR_HEIGHT/2/4]; 2598 return FrogNumNew(p->z); 2599 } 2600 AddMethod("C3DWorldManager","screenCoordToZ",&ScreenCoordToZ); 2601 C3DPoint *TileAtScreen(C3DWorldManager *man,I64 *argv,I64 argc) { 2602 C3DPoint *r=ConstructThing("C3DPoint"); 2603 r->x=man->cur_tile_x; 2604 r->y=man->cur_tile_y; 2605 r->z=man->cur_tile_z; 2606 return r; 2607 } 2608 AddMethod("C3DWorldManager","screenTile",&TileAtScreen); 2609 C3DPoint *TileSideAtScreen(C3DWorldManager *man,I64 *argv,I64 argc) { 2610 C3DPoint *r=ConstructThing("C3DPoint"); 2611 r->x=man->cur_tile_side_x; 2612 r->y=man->cur_tile_side_y; 2613 r->z=man->cur_tile_side_z; 2614 return r; 2615 } 2616 AddMethod("C3DWorldManager","screenTileSide",&TileSideAtScreen); 2617 // 2618 // World Edit section 2619 // 2620 2621 CFrogNum *WorldManagerGetShiftKey(C3DWorldManager *,I64 *argv,I64 argc) { 2622 return FrogNumNew(Bt(kbd.down_bitmap,SC_SHIFT)); 2623 } 2624 AddMethod("C3DWorldManager","getShiftKey",&WorldManagerGetShiftKey); 2625 2626 CFrogNum *WorldManagerGetTileAtXAtYAtZ(C3DWorldManager *,I64 *argv,I64 argc) { 2627 I64 x=Floor(AsF64(argv[0])); 2628 I64 y=Floor(AsF64(argv[1])); 2629 I64 z=Floor(AsF64(argv[2])); 2630 return FrogNumNew(BlockAtXYZ(x,y,z)); 2631 } 2632 AddMethod("C3DWorldManager","getTileAtX:atY:atZ:",&WorldManagerGetTileAtXAtYAtZ); 2633 CFrogNum *WorldManagerSetTileAtXAtYAtZ(C3DWorldManager *,I64 *argv,I64 argc) { 2634 I64 x=Floor(AsF64(argv[1])); 2635 I64 y=Floor(AsF64(argv[2])); 2636 I64 z=Floor(AsF64(argv[3])); 2637 if(0<=x<w->world_width) 2638 if(0<=y<w->world_height) 2639 if(I8_MIN<=z<I8_MAX) { 2640 w->world_blocks[(x+y*w->world_width)*256+z-I8_MIN]=AsF64(argv[0]); 2641 } 2642 return FROG_SMALL_NIL; 2643 } 2644 AddMethod("C3DWorldManager","setTile:atX:atY:atZ:",&WorldManagerSetTileAtXAtYAtZ); 2645 2646 2647 // 2648 // Test section 2649 // 2650 #ifdef TEST_3D 2651 DocClear; 2652 #include "UI.HC" 2653 InitWorld; 2654 I64 mp_thing_done=0; 2655 CFrogThing *UpdateParticles(C3DWorldManager *man,I64 *argv,I64 argc) { 2656 if(man->is_paused) return; 2657 I64 tx,ty; 2658 F64 fx,cx,fy,cy,dx,dy; 2659 static CFrogClass *ws_class=FrogClassNew(UniverseAddClass("CWallStain")); 2660 C3DParticle *head,*p,*n; 2661 head=&w->particles; 2662 for(p=head->next;head!=p;p=n) { 2663 n=p->next; 2664 if(!p->hit&&BlockAtXYZ(p->x/128,p->y/128,Floor(p->z))) { 2665 //Pick nearest "wall" 2666 fx=Floor(p->x/128.); 2667 cx=Ceil(p->x/128.); 2668 fy=Floor(p->y/128.); 2669 cy=Ceil(p->y/128.); 2670 dx=Min(cx-p->x/128.,p->x/128.-fx); 2671 dy=Min(cy-p->y/128.,p->y/128.-fy); 2672 if(dy<dx) { 2673 if(p->y/128-fy>.5) 2674 p->y=(fy-.1)*128; 2675 else 2676 p->y=(cy+.1)*128; 2677 } else { 2678 if(p->x/128.-fx>.5) 2679 p->x=(fx-.1)*128; 2680 else 2681 p->x=(cx+.1)*128; 2682 } 2683 if(!p->hit) 2684 goto hit; 2685 } 2686 if(C3DWorldMoveWithCollision( 2687 w, 2688 Arg(p->momx,p->momy), 2689 Sqrt(p->momx*p->momx+p->momy*p->momy), 2690 &p->x, 2691 &p->y, 2692 FALSE, 2693 p->z 2694 )) { 2695 goto hit; 2696 } 2697 if(C3DWorldMoveZWithCollision(w,p->momz,p->x,p->y,&p->z)) { 2698 goto hit; 2699 } 2700 p->momx*=20/30.; 2701 p->momy*=20/30.; 2702 p->momz-=.2; 2703 tx=p->x/GRID_SZ; 2704 ty=p->y/GRID_SZ; 2705 if(0) { 2706 hit: 2707 if(p->momz<=0.) { 2708 p->z=Floor(p->z)+.001; //Sit on floors 2709 p->hit=1; 2710 p->momz=0; 2711 stain: 2712 if(p->splat_lump_name[0]) { 2713 CallScript("newStainAtX:atY:atZ:withGraphicsLump:withSpread:",ws_class, 2714 FrogNumNew(p->x), 2715 FrogNumNew(p->y), 2716 FrogNumNew(p->z), 2717 FrogStrNew(p->splat_lump_name), 2718 FrogNumNew(32.) 2719 ); 2720 p->splat_lump_name[0]=0; 2721 } 2722 } 2723 } 2724 if(Frog_tS>1+p->born_at) { 2725 QueRem(p); 2726 Free(p); 2727 w->particle_cnt--; 2728 } 2729 } 2730 return FROG_SMALL_NIL; 2731 } 2732 AddMethod("C3DWorldManager","updateParticles",&UpdateParticles); 2733 U0 Draw3DWorld(CTask *,CDC *dc) { 2734 C3DWorldManager *man=FrogSymbol("world")->value; 2735 static CFrogClass *ws_class=FrogClassNew(UniverseAddClass("CWallStain")); 2736 man->world_handle=w; 2737 w->_wall_stains=man->wall_stains->items; 2738 w->_floor_stains=man->floor_stains->items; 2739 w->_2d_things=man->things->items; 2740 w->to_dc=dc; 2741 CDoor **doors; 2742 CDayNightStar **stars,*star; 2743 CPlayer *player=CallScript("getPlayer",man); 2744 if(player==FROG_SMALL_NIL) { 2745 return; 2746 } 2747 CFrogImg *hand_gr,*minimap; 2748 C3DLine *line; 2749 I32 *odepth_buf; 2750 I64 tx,ty,c,f; 2751 CI64Set *s; 2752 C3DParticle *p,*head,*n; 2753 CShellCasing *shellc; 2754 CHandItem *hand=FrogSymbol("hand_item")->value; 2755 w->angle2=player->angle2; 2756 w->angle=player->angle; 2757 w->x=player->x; 2758 w->y=player->y; 2759 w->cam_height=player->z+.6; 2760 DCFill(dc,man->day_night->sky_color); 2761 stars=man->day_night->stars->items->body; 2762 c=man->day_night->stars->items->cnt; 2763 while(--c>=0) { 2764 star=stars[c]; 2765 dc->color=star->color; 2766 GrFillCircle(dc,star->screen_x,star->screen_y,,star->radius/2); 2767 } 2768 DCDepthBufRst(dc); 2769 PrepareThingsForDraw(man); 2770 odepth_buf=dc->depth_buf; 2771 C3DWorldCastRays(w); 2772 mp_thing_done=0; 2773 C3DLine ***body; 2774 I64 idx; 2775 head=&w->particles; 2776 for(p=head->next;head!=p;p=n) { 2777 n=p->next; 2778 DrawParticle(w,p); 2779 } 2780 doors=man->doors->items->body; 2781 c=man->doors->items->cnt; 2782 //Rely on wall commands to choose what part of door to draw 2783 I64 cnt=man->lines->items->cnt; 2784 body=MAllocIdent(man->lines->items->body); 2785 for(idx=0;idx!=cnt;idx++) { 2786 line=body[idx]; 2787 dc->color=line->color; 2788 dc->thick=line->thick; 2789 Line3D(w,dc,&line->x0,&line->x); 2790 } 2791 Free(body); 2792 w->to_dc->depth_buf=NULL; 2793 2794 Fs->user_data=0; 2795 if(ThingHasClass(player,"CPlayer")) { 2796 if(ThingHasClass(player->shell_casings,"CFrogArray")) { 2797 s=player->shell_casings->items; 2798 body=s->body; 2799 idx=s->cnt; 2800 while(--idx>=0) { 2801 shellc=body[idx]; 2802 if(ThingHasClass(shellc,"CShellCasing")) { 2803 if(ThingHasClass(shellc->dc,"CFrogImg")) 2804 AnimDCBlot(dc,shellc->x,shellc->y,shellc->dc->dc); 2805 } 2806 } 2807 } 2808 } 2809 2810 if(ThingHasClass(hand,"CHandItem")) { 2811 if(ThingHasClass(hand_gr=hand->gr,"CFrogImg")) { 2812 c=AnimDCCnt(hand_gr->dc); 2813 f=(Frog_tS-hand->anim_start_tS)/ANIM_DELAY; 2814 if(hand->anim_no_repeat) { 2815 if(f>=c) 2816 f=c-1; 2817 else 2818 f%=c; 2819 } else 2820 f%=c; 2821 GrBlot(dc,hand->x+hand->xoff-hand_gr->dc->width/2,hand->y+hand->yoff-hand_gr->dc->height/2,hand_gr->dc+f); 2822 } 2823 } 2824 fin: 2825 /* minimap=MakeMinimap1(man,player->x,player->y,player->angle,64,player->z); 2826 GrBlot(dc,GR_WIDTH-128-16,30,minimap); 2827 DCDel(minimap);*/ 2828 dc->depth_buf=odepth_buf; 2829 w->to_dc=NULL; 2830 } 2831 2832 CFrogThing *WorldManagerEmptyWithDim(C3DWorldManager *man,I64 *argv,I64 argc) { 2833 w->world_width=AsF64(argv[0]); 2834 w->world_height=AsF64(argv[1]); 2835 Free(w->world_blocks); 2836 Free(w->world_lighting); 2837 w->world_lighting=CAlloc(256*(w->world_width*w->world_height),frog_mem_task); 2838 w->world_blocks=CAlloc(256*(w->world_width*w->world_height),frog_mem_task); 2839 CFrogDictionary *old_templates=man->thing_templates,*ot2=man->tile_templates; 2840 CallScript("initGrid",man); 2841 man->thing_templates=old_templates; 2842 man->tile_templates=ot2; 2843 w->_wall_stains=man->wall_stains->items; 2844 w->_floor_stains=man->floor_stains->items; 2845 w->_2d_things=man->things->items; 2846 return man; 2847 } 2848 AddMethod("C3DWorldManager","emptyWorldWithWidth:withHeight:",&WorldManagerEmptyWithDim); 2849 CFrogThing *WorldPhysics(C3DWorldManager *wm,I64 *argv,I64 argc) { 2850 C3DThing **body=MAllocIdent(wm->things->items->body),*thing,*player; 2851 player=CallScript("getPlayer",wm); 2852 I64 c=wm->things->items->cnt,idx; 2853 F64 t,now=AsF64(FrogTimeNow(FROG_SMALL_NIL,NULL,0)->ts); 2854 //Heres the deal. 2855 //If we are a client,we will let the server do the physics on everyting except the player 2856 if(wm->is_client) { 2857 if(ThingHasClass(player,"CPlayer")) { 2858 PhysicsOnThing(w,player,wm,0.); 2859 player->momx2=0; 2860 player->momy2=0; 2861 player->momz2=0; 2862 //interoplate things 2863 for(idx=0;idx<c;idx++) { 2864 thing=body[idx]; 2865 if(thing!=player) { 2866 //interpolate_end_tS is the time we recived,start_tS is the frame before that 2867 t=(now-thing->interpolate_end_tS)/(thing->interpolate_end_tS-thing->interpolate_start_tS); 2868 thing->x=Lerp(t,thing->server_old_x,thing->server_new_x); 2869 thing->y=Lerp(t,thing->server_old_y,thing->server_new_y); 2870 thing->z=Lerp(t,thing->server_old_z,thing->server_new_z); 2871 } 2872 } 2873 } 2874 Free(body); 2875 return FROG_SMALL_NIL; 2876 } 2877 for(idx=0;idx<c;idx++) { 2878 thing=body[idx]; 2879 //Only update when in view 2880 if(Sqr(VIEW_DIST*GRID_SZ) 2881 >Sqr(thing->y-player->y)+Sqr(thing->x-player->x)) { 2882 PhysicsOnThing(w,thing,wm,0.); 2883 if(body[idx]==player) { 2884 player->momx2=0; 2885 player->momy2=0; 2886 player->momz2=0; 2887 } 2888 } 2889 } 2890 Free(body); 2891 return FROG_SMALL_NIL; 2892 } 2893 AddMethod("C3DWorldManager","physics",&WorldPhysics); 2894 2895 2896 2897 CFrogThing *TileTemplateUpdate(CTileTemplate *tile,I64 *argv,I64 argc) { 2898 I64 idx=tile->tile_idx; 2899 CDC *dc; 2900 if(dc=w->wall_textures[idx]) 2901 AnimDCDel(dc); 2902 if(dc=w->floor_textures[idx]) 2903 AnimDCDel(dc); 2904 if(dc=w->ceil_textures[idx]) 2905 AnimDCDel(dc); 2906 if(ThingHasClass(tile->wall_texture,"CFrogImg")) { 2907 w->wall_textures[idx]=ScaleDC(tile->wall_texture->dc,128,128,frog_mem_task); 2908 } else 2909 w->wall_textures[idx]=NULL; 2910 2911 if(ThingHasClass(tile->floor_texture,"CFrogImg")) { 2912 w->floor_textures[idx]=ScaleDC(tile->floor_texture->dc,128,128,frog_mem_task); 2913 } else 2914 w->floor_textures[idx]=NULL; 2915 2916 if(ThingHasClass(tile->ceil_texture,"CFrogImg")) { 2917 w->ceil_textures[idx]=ScaleDC(tile->ceil_texture->dc,128,128,frog_mem_task); 2918 } else 2919 w->ceil_textures[idx]=NULL; 2920 return FROG_SMALL_NIL; 2921 } 2922 AddMethod("CTileTemplate","updateTextures",&TileTemplateUpdate); 2923 2924 CFrogNum *WallStainPlaceAtXYZ(CWallStain *ws,I64 *argv,I64 argc) { 2925 F64 x=AsF64(argv[0])/128; 2926 F64 y=AsF64(argv[1])/128; 2927 F64 z=AsF64(argv[2]); 2928 F64 fz=Floor(z); 2929 F64 cz=Ceil(z); 2930 F64 fx=Floor(x); 2931 F64 cx=Ceil(x); 2932 F64 fy=Floor(y); 2933 F64 cy=Ceil(y); 2934 2935 ws->tile_x=fx; 2936 ws->tile_y=fy; 2937 ws->tile_z=fz; 2938 2939 //Realy close to z==fz,so test for empty side 2940 if(Abs(z-fz)<1./256) { 2941 if(0==BlockAtXYZ(ws->tile_x,ws->tile_y,ws->tile_z)) 2942 goto fzc; 2943 } 2944 if(Abs(z-cz)<1./256) { 2945 if(0==BlockAtXYZ(ws->tile_x,ws->tile_y,ws->tile_z-1)) { 2946 ws->tile_z--; 2947 goto czc; 2948 } 2949 } 2950 if(Abs(x-fx)<1./256) { 2951 if(0==BlockAtXYZ(ws->tile_x,ws->tile_y,ws->tile_z)) 2952 goto fxc; 2953 } 2954 if(Abs(x-cx)<1./256) { 2955 if(0==BlockAtXYZ(ws->tile_x-1,ws->tile_y,ws->tile_z)) { 2956 ws->tile_x--; 2957 goto cxc; 2958 } 2959 } 2960 if(Abs(y-fy)<1./256) { 2961 if(0==BlockAtXYZ(ws->tile_x,ws->tile_y,ws->tile_z)) 2962 goto fyc; 2963 } 2964 if(Abs(y-cy)<1./256) { 2965 if(0==BlockAtXYZ(ws->tile_x,ws->tile_y-1,ws->tile_z)) { 2966 ws->tile_y--; 2967 goto cyc; 2968 } 2969 } 2970 2971 if(z<fz+.1) { 2972 fzc: 2973 ws->sidez=1; 2974 ws->center_x=ToI64(x*128-ws->tile_x*128); 2975 ws->center_y=ToI64(y*128-ws->tile_y*128); 2976 ws->tile_z--; 2977 final: 2978 if(BlockAtXYZ(ws->tile_x,ws->tile_y,ws->tile_z)) { 2979 return FrogNumNew(0==BlockAtXYZ(ws->tile_x+ws->sidex,ws->tile_y+ws->sidey,ws->tile_z+ws->sidez)); 2980 } 2981 return FrogNumNew(0); 2982 } 2983 2984 if(z>cz-.1) { 2985 czc: 2986 ws->sidez=-1; 2987 ws->center_x=ToI64(x*128-ws->tile_x*128); 2988 ws->center_y=ToI64(y*128-ws->tile_y*128); 2989 ws->tile_z++; 2990 goto final; 2991 } 2992 if(x<fx+.1) { 2993 fxc: 2994 ws->sidex=1; 2995 ws->center_x=y*128-ws->tile_y*128; 2996 ws->center_y=z*128-ws->tile_z*128; 2997 ws->tile_x--; 2998 goto final; 2999 } 3000 if(x>cx-.1) { 3001 cxc: 3002 ws->sidex=-1; 3003 ws->center_x=y*128-ws->tile_y*128; 3004 ws->center_y=z*128-ws->tile_z*128; 3005 ws->tile_x++; 3006 goto final; 3007 } 3008 if(y<fy+.1) { 3009 fyc: 3010 ws->sidey=1; 3011 ws->center_x=x*128-ws->tile_x*128; 3012 ws->center_y=z*128-ws->tile_z*128; 3013 ws->tile_y--; 3014 goto final; 3015 } 3016 if(y>cy-.1) { 3017 cyc: 3018 ws->sidey=-1; 3019 ws->center_x=x*128-ws->tile_x*128; 3020 ws->center_y=z*128-ws->tile_z*128; 3021 ws->tile_y++; 3022 goto final; 3023 } 3024 return FrogNumNew(0); 3025 } 3026 AddMethod("CWallStain","placeAtX:atY:atZ:",&WallStainPlaceAtXYZ); 3027 CWallStain *WallStainTryMerge(CWallStain *have) { 3028 C3DWorldManager *man=FrogSymbol("world")->value; 3029 I64 cnt; 3030 CWallStain **body,*got; 3031 CFrogArray *arr=CallScript("at:", 3032 CallScript("at:",man->stains_grid, 3033 FrogNumNew(have->tile_x/4)), 3034 FrogNumNew(have->tile_y/4) 3035 ); 3036 arr=man->wall_stains; 3037 if(ThingHasClass(arr,"CFrogArray")) { 3038 cnt=arr->items->cnt; 3039 body=arr->items->body; 3040 while(--cnt>=0) { 3041 got=body[cnt]; 3042 if(got->tile_x==have->tile_x&& 3043 got->tile_y==have->tile_y&& 3044 got->tile_z==have->tile_z&& 3045 got->sidex==have->sidex&& 3046 got->sidey==have->sidey&& 3047 got->sidez==have->sidez 3048 ) { 3049 return got; 3050 } 3051 } 3052 } 3053 got=ConstructThing("CWallStain"); 3054 got->tile_x=have->tile_x; 3055 got->tile_y=have->tile_y; 3056 got->tile_z=have->tile_z; 3057 got->sidex=have->sidex; 3058 got->sidey=have->sidey; 3059 got->sidez=have->sidez; 3060 CallScript("addWallStain:",man,got); 3061 return got; 3062 } 3063 CFrogNum *WallStainPutGraphics(CWallStain *ws,I64 *argv,I64 argc) { 3064 C3DWorldManager *man=FrogSymbol("world")->value; 3065 CFrogStr *lump_name=argv[0]; 3066 CFrogImg *img=CallScript("get:", 3067 FrogClassNew(UniverseAddClass("CFrogImg")), 3068 lump_name 3069 ); 3070 CWallStain *new; 3071 F64 x=AsF64(argv[1]); 3072 F64 y=AsF64(argv[2]); 3073 I64 xx,yy,zz; 3074 I64 ox=ws->tile_x,oy=ws->tile_y,oz=ws->tile_z; 3075 if(!ws->sidez) { 3076 for(zz=-1;zz<=1;zz++) { 3077 for(xx=-1;xx<=1;xx+=2) { 3078 if(BlockAtXYZ(ox+xx,oy,oz+zz)) { 3079 ws->tile_x=ox+xx; 3080 ws->tile_y=oy; 3081 ws->tile_z=oz+zz; 3082 new=WallStainTryMerge(ws); 3083 if(AsF64(CallScript("addGraphics:atX:atY:",new,img, 3084 FrogNumNew(x-xx*128), 3085 FrogNumNew(y-zz*128)))) { 3086 } 3087 } 3088 } 3089 for(yy=-1;yy<=1;yy+=2) { 3090 if(BlockAtXYZ(ox,oy+yy,oz+zz)) { 3091 ws->tile_x=ox; 3092 ws->tile_y=oy+yy; 3093 ws->tile_z=oz+zz; 3094 new=WallStainTryMerge(ws); 3095 if(AsF64(CallScript("addGraphics:atX:atY:",new,img, 3096 FrogNumNew(x-yy*128), 3097 FrogNumNew(y-zz*128)))) { 3098 } 3099 } 3100 } 3101 } 3102 } 3103 if(ws->sidez) 3104 for(yy=-1;yy<=1;yy++) { 3105 for(xx=-1;xx<=1;xx++) { 3106 if(xx||yy) { 3107 ws->tile_x=ox+xx; 3108 ws->tile_y=oy+yy; 3109 ws->tile_z=oz; 3110 new=WallStainTryMerge(ws); 3111 if(AsF64(CallScript("addGraphics:atX:atY:",new,img, 3112 FrogNumNew(x-xx*128), 3113 FrogNumNew(y-yy*128)))) { 3114 } 3115 } 3116 } 3117 } 3118 ws->tile_x=ox; 3119 ws->tile_y=oy; 3120 ws->tile_z=oz; 3121 new=WallStainTryMerge(ws); 3122 if(AsF64(CallScript("addGraphics:atX:atY:",new,img, 3123 FrogNumNew(x), 3124 FrogNumNew(y)))) { 3125 } 3126 } 3127 AddMethod("CWallStain","putGraphics:atX:atY:",&WallStainPutGraphics); 3128 // 3129 // 3130 // 3131 #if __CMD_LINE__ 3132 CallScript("newLandscapeWithWidth:withHeight:",FrogSymbol("world")->value,FrogNumNew(10),FrogNumNew(10)); 3133 C3DWorldShell *shell=ConstructThing("C3DWorldShell"); 3134 CallScript("init",shell); 3135 UIShellRun(shell,NULL,0); 3136 #endif 3137 #endif 3138 #endif