<1>/* Graphics Not Rendered in HTML */ <2>/* Graphics Not Rendered in HTML */ <3>/* Graphics Not Rendered in HTML */ #define STARS_NUM 8192 I64 stars_x[STARS_NUM], stars_y[STARS_NUM]; #define RADIUS 7 class MyMass:CMass { U8 *img; }; class MySpring:CSpring { I64 type, action_key; }; //Main Modses #define MMD_EDIT 0 #define MMD_PLAY 1 #define MMD_MODES_NUM 2 CColorROPU32 main_mode_colors[MMD_MODES_NUM] = {LTRED, LTGREEN}; DefineListLoad("ST_MAIN_MODES", "Edit\0Play\0"); CCtrlBttnState main_mode_bttn; //Edit Modes #define EMD_MASS 0 #define EMD_SPRING 1 #define EMD_CONNECTOR 2 #define EMD_THRUSTER 3 #define EMD_MOVE 4 #define EMD_MODES_NUM 5 CColorROPU32 edit_mode_colors[EMD_MODES_NUM] = {LTGRAY, LTCYAN, CYAN, YELLOW, LTBLUE}; DefineListLoad("ST_EDIT_MODES", "Mass\0Spring\0Connector\0Thruster\0Move\0"); CCtrlBttnState edit_mode_bttn; CTask *task; F64 zoom; I64 next_action_key, action_scan_codes[10]; CMathODE *ode = NULL; U0 S2W(F64 sx, F64 sy, F64 *_wx, F64 *_wy) { sx -= task->pix_left + task->scroll_x; sy -= task->pix_top + task->scroll_y; *_wx = sx / zoom; *_wy = sy / zoom; } U0 W2S(F64 wx, F64 wy, F64 *_sx, F64 *_sy) { *_sx = wx * zoom; *_sy = wy * zoom; } #define ZOOM_STEPS 20 U0 Zoom(F64 d) { F64 sx, sy, wx, wy; I64 i, x = mouse.pos.x, y = mouse.pos.y; d = Exp(Ln(d) / ZOOM_STEPS); for (i = 0; i < ZOOM_STEPS; i++) { S2W(x, y, &wx, &wy); zoom = Clamp(zoom * d, 0.02, 50); W2S(wx, wy, &sx, &sy); task->scroll_x = mouse.pos.x - sx-task->pix_left; task->scroll_y = mouse.pos.y - sy-task->pix_top; Sleep(10); } } U0 DrawIt(CTask *, CDC *dc) { I64 i; F64 theta, d, x1, y1, x2, y2; MyMass *tmpm; MySpring *tmps; tmpm = ode->next_mass; if (tmpm != &ode->next_mass) { task->scroll_x = -tmpm->x * zoom + task->pix_width >> 1; task->scroll_y = -tmpm->y * zoom + task->pix_height >> 1; } dc->flags |= DCF_TRANSFORMATION; Mat4x4Scale(dc->r, zoom); switch (main_mode_bttn.state) { case MMD_EDIT: task->text_attr = DKGRAY << 4 + WHITE; dc->color = BLACK; break; case MMD_PLAY: task->text_attr = BLACK << 4 + WHITE; dc->color = WHITE; for (i = 0; i < STARS_NUM; i++) GrPlot3(dc, stars_x[i], stars_y[i], 0); break; } if (main_mode_bttn.state == MMD_EDIT) { if (edit_mode_bttn.state == EMD_CONNECTOR) { dc->color = CYAN; S2W(FONT_WIDTH * 11, FONT_HEIGHT * 7, &x1, &y1); GrPutChar3(dc, x1, y1, 0, next_action_key); } else if (edit_mode_bttn.state == EMD_THRUSTER) { dc->color = YELLOW; S2W(FONT_WIDTH * 11, FONT_HEIGHT * 7, &x1, &y1); GrPutChar3(dc, x1, y1, 0, next_action_key); } } tmps = ode->next_spring; while (tmps != &ode->next_spring) { if (tmps->type == EMD_SPRING) { dc->color = LTCYAN; GrLine3(dc, tmps->end1->x, tmps->end1->y, 0, tmps->end2->x, tmps->end2->y, 0); } else if (tmps->type == EMD_CONNECTOR) { dc->color = CYAN; GrLine3(dc, tmps->end1->x, tmps->end1->y, 0, tmps->end2->x, tmps->end2->y, 0); } tmps = tmps->next; } dc->color = LTGRAY; tmpm = ode->next_mass; while (tmpm != &ode->next_mass) { Sprite3(dc, tmpm->x, tmpm->y, 0, tmpm->img); tmpm = tmpm->next; } tmps = ode->next_spring; while (tmps != &ode->next_spring) { x1 = tmps->end1->x; y1 = tmps->end1->y; x2 = tmps->end2->x; y2 = tmps->end2->y; if (tmps->type == EMD_THRUSTER) { theta = Arg(x2 - x1, y2 - y1); if (Bt(kbd.down_bitmap, action_scan_codes[tmps->action_key - '0'])) { dc->flags |= DCF_SYMMETRY; DCSymmetry3Set(dc, x1, y1, 256, x1, y1, 0, x1 - 256 * Cos(theta), y1 - 256 * Sin(theta), 0); for (i = 0; i < 8; i++) { d = 20 * Rand + 6; if (d < 10) dc->color = BLUE; else if (d < 16) dc->color = LTBLUE; else dc->color = YELLOW; GrLine3(dc, x1 - 3 * Cos(theta), y1 - 3 * Sin(theta), 0, x1 - d * Cos(theta) + 0.5 * d * Sin(theta), y1 - d * Sin(theta) - 0.5 * d * Cos(theta), 0); GrLine3(dc, x1 - 2 * d * Cos(theta), y1 - 2 * d * Sin(theta), 0, x1 - d * Cos(theta) + 0.5 * d * Sin(theta), y1 - d * Sin(theta) - 0.5 * d * Cos(theta), 0); } dc->flags &= ~DCF_SYMMETRY; } Sprite3ZB(dc, x1, y1, 0, <3>, theta); if (zoom > 0.5) { dc->color = YELLOW; GrPutChar3(dc, (x1 * 7 + x2) / 8, (y1 * 7 + y2) / 8, 0, tmps->action_key); } } else if (tmps->type == EMD_CONNECTOR) { if (zoom > 0.5) { dc->color = CYAN; GrPutChar3(dc, (x1 * 7 + x2) / 8, (y1 * 7 + y2) / 8, 0, tmps->action_key); } } tmps = tmps->next; } } U0 MyDerivative(CMathODE *ode, F64, COrder2D3 *, COrder2D3 *) { //The forces due to springs and drag are //automatically handled by the //ode code. We can add new forces //here. F64 d, dd; CD3 p; MyMass *tmpm1, *tmpm2; MySpring *tmps; tmpm1 = ode->next_mass; while (tmpm1 != &ode->next_mass) { tmpm2 = tmpm1->next; while (tmpm2 != &ode->next_mass) { D3Sub(&p, &tmpm2->state->x, &tmpm1->state->x); dd = D3NormSqr(&p); if (dd <= Sqr(2 * RADIUS)) { d = Sqrt(dd) + 0.0001; dd = 10.0 * Sqr(Sqr(Sqr(2 * RADIUS) - dd)); D3MulEqu(&p, dd / d); D3AddEqu(&tmpm2->DstateDt->DxDt, &p); D3SubEqu(&tmpm1->DstateDt->DxDt, &p); } tmpm2 = tmpm2->next; } tmpm1 = tmpm1->next; } tmps = ode->next_spring; while (tmps != &ode->next_spring) { if (main_mode_bttn.state == MMD_PLAY && tmps->type == EMD_THRUSTER && Bt(kbd.down_bitmap, action_scan_codes[tmps->action_key - '0'])) { D3Sub(&p, &tmps->end2->state->x, &tmps->end1->state->x); D3Unit(&p); D3MulEqu(&p, 2000); D3AddEqu(&tmps->end1->DstateDt->DxDt, &p); } tmps = tmps->next; } } MyMass *PlaceMass(I64 x, I64 y) { MyMass *tmpm=CAlloc(sizeof(MyMass)); tmpm->mass = 1.0; tmpm->drag_profile_factor = 100.0; tmpm->x = x; tmpm->y = y; QueueInsert(tmpm, ode->last_mass); return tmpm; } MySpring *PlaceSpring(MyMass *tmpm1, MyMass *tmpm2, I64 type) { MySpring *tmps = CAlloc(sizeof(MySpring)); F64 d = D3Dist(&tmpm1->x, &tmpm2->x); tmps->end1 = tmpm1; tmps->end2 = tmpm2; tmps->rest_len = d; tmps->type = type; if (type == EMD_THRUSTER) tmps->const = 0; else tmps->const = 2500000/Sqr(d); tmps->action_key = next_action_key; QueueInsert(tmps, ode->last_spring); return tmps; } U0 CenterMasses() { CD3 p; MyMass *tmpm1, *tmpm2; tmpm1 = ode->next_mass; if (tmpm1 != &ode->next_mass) { D3Copy(&p, &tmpm1->x); tmpm2 = ode->next_mass; while (tmpm2 != &ode->next_mass) { D3SubEqu(&tmpm2->x, &p); tmpm2 = tmpm2->next; } } } U0 NullSprings() { MySpring *tmps = ode->next_spring; while (tmps != &ode->next_spring) { tmps->rest_len = D3Dist(&tmps->end1->x, &tmps->end2->x); tmps = tmps->next; } } U0 BreakConnectors() { MySpring *tmps = ode->next_spring, *tmps1; while (tmps != &ode->next_spring) { tmps1 = tmps->next; if (tmps->type == EMD_CONNECTOR && Bt(kbd.down_bitmap, action_scan_codes[tmps->action_key - '0'])) { QueueRemove(tmps); Free(tmps); } tmps = tmps1; } } U0 Init() { I64 i; task = Fs; for (i = 0; i <= 9; i++) action_scan_codes[i] = Char2ScanCode('0' + i); for (i = 0; i < STARS_NUM; i++) { stars_x[i] = RandU32 % 8192 - 4096; stars_y[i] = RandU32 % 8192 - 4096; } next_action_key = '1'; zoom = 1.0; ode = ODENew(0, 1e-4, ODEF_HAS_MASSES); ode->derive = &MyDerivative; ode->acceleration_limit = 5e3; ode->drag_v2 = 0.000002; ode->drag_v3 = 0.0000001; QueueInsert(ode, Fs->last_ode); } U0 CleanUp() { if (ode) { QueueRemove(ode); QueueDel(&ode->next_mass, TRUE); QueueDel(&ode->next_spring, TRUE); ODEDel(ode); ode = NULL; } } U0 MyNoise(I64 mS, F64 min_ona, F64 max_ona) {//Make white noise for given number of mS. // See Noise. On bare-metal, Spawn() hogs CPU. CSoundEffectFrame *ns; if (mS > 0) { ns = MAlloc(sizeof(CSoundEffectFrame)); ns->type = SE_NOISE; ns->duration = mS / 1000.0; ns->ona1 = min_ona; ns->ona2 = max_ona; music.mute++; SoundEffectTask(ns); Sound; } return; } U0 PlayShip() { I64 arg1, arg2; F64 last_noise = 0; Bool okay; ODEPause(ode, OFF); MenuPush( "File {" " Abort(,CH_SHIFT_ESC);" " Exit(,CH_ESC);" "}" "Edit {" " EditShip(,CH_SPACE);" "}" "Play {" " Center(,'c');" " Damp(,'d');" " Action0(,'0');" " Action1(,'1');" " Action2(,'2');" " Action3(,'3');" " Action4(,'4');" " Action5(,'5');" " Action6(,'6');" " Action7(,'7');" " Action8(,'8');" " Action9(,'9');" "}" "View {" " ZoomIn(,'z');" " ZoomOut(,'Z');" "}" ); DocClear; try { while (main_mode_bttn.state == MMD_PLAY) { BreakConnectors; switch (MessageScan(&arg1, &arg2, 1 << MESSAGE_KEY_DOWN| 1 << MESSAGE_KEY_UP)) { case MESSAGE_KEY_DOWN: switch (arg1) { case '0'...'9': if (tS > last_noise + 0.25) { MyNoise(250, 18, 46); last_noise = tS; } break; case 'z': Spawn(&Zoom, 2.0(I64)); break; case 'Z': Spawn(&Zoom, 0.5(I64)); break; case 'c': CenterMasses; break; case 'd': ode->drag_v2 = 0.002; ode->drag_v3 = 0.0001; break; case CH_SPACE: if (++main_mode_bttn.state == MMD_MODES_NUM) main_mode_bttn.state = 0; MessageGet(,, 1 << MESSAGE_KEY_UP); goto ps_done; case CH_SHIFT_ESC: case CH_ESC: throw; } break; case MESSAGE_KEY_UP: switch (arg1) { case 'd': ode->drag_v2 = 0.000002; ode->drag_v3 = 0.0000001; break; } break; } Refresh; } ps_done: //Don't goto out of try okay = TRUE; } catch { Fs->catch_except = TRUE; okay = FALSE; } MenuPop; if (!okay) throw; } U0 EditShip() { I64 arg1, arg2; F64 wx, wy; Bool okay; MyMass *tmpm1 = NULL, *tmpm2 = NULL; CCtrl *bt_edit_mode = CtrlBttnNew(0, 5 * FONT_HEIGHT + 4, 80,, EMD_MODES_NUM, Define("ST_EDIT_MODES"), edit_mode_colors, &edit_mode_bttn); ODEPause(ode); MenuPush( "File {" " Abort(,CH_SHIFT_ESC);" " Exit(,CH_ESC);" "}" "Play {" " PlayShip(,CH_SPACE);" " Center(,'c');" " Damp(,'d');" "}" "Edit {" " Move(,'v');" " Mass(,'m');" " Spring(,'s');" " Connector(,'n');" " Thruster(,'t');" " Restart(,'\n');" " Action0(,'0');" " Action1(,'1');" " Action2(,'2');" " Action3(,'3');" " Action4(,'4');" " Action5(,'5');" " Action6(,'6');" " Action7(,'7');" " Action8(,'8');" " Action9(,'9');" "}" "View {" " ZoomIn(,'z');" " ZoomOut(,'Z');" "}" ); DocClear; try { while (main_mode_bttn.state == MMD_EDIT) { switch (MessageScan(&arg1, &arg2, 1 << MESSAGE_MS_L_DOWN | 1 << MESSAGE_MS_L_UP | 1 << MESSAGE_MS_R_DOWN| 1 << MESSAGE_MS_MOVE | 1 << MESSAGE_KEY_DOWN | 1 << MESSAGE_KEY_UP)) { case MESSAGE_MS_L_DOWN: switch (edit_mode_bttn.state) { case EMD_MASS: S2W(mouse.pos.x, mouse.pos.y, &wx, &wy); tmpm1 = PlaceMass(wx, wy); if (ode->next_mass == tmpm1) tmpm1->img = <1>; else tmpm1->img = <2>; tmpm1 = NULL; break; case EMD_SPRING: case EMD_CONNECTOR: case EMD_THRUSTER: case EMD_MOVE: S2W(mouse.pos.x, mouse.pos.y, &wx, &wy); tmpm1 = MassFind(ode, wx, wy); tmpm2 = NULL; break; } break; case MESSAGE_MS_L_UP: switch (edit_mode_bttn.state) { case EMD_MASS: break; case EMD_SPRING: case EMD_CONNECTOR: case EMD_THRUSTER: S2W(mouse.pos.x, mouse.pos.y, &wx, &wy); if (tmpm1 && (tmpm2 = MassFind(ode, wx, wy)) && tmpm1 != tmpm2) PlaceSpring(tmpm1, tmpm2, edit_mode_bttn.state); tmpm1 = tmpm2 = NULL; break; case EMD_MOVE: S2W(mouse.pos.x, mouse.pos.y, &wx, &wy); if (tmpm1) { tmpm1->x = wx; tmpm1->y = wy; tmpm1->z = 0; NullSprings; } tmpm1 = tmpm2 = NULL; break; } break; case MESSAGE_MS_MOVE: switch (edit_mode_bttn.state) { case EMD_MOVE: S2W(mouse.pos.x, mouse.pos.y, &wx, &wy); if (tmpm1) { tmpm1->x = wx; tmpm1->y = wy; tmpm1->z = 0; NullSprings; } break; } break; case MESSAGE_MS_R_DOWN: if (++edit_mode_bttn.state == EMD_MODES_NUM) edit_mode_bttn.state = 0; break; case MESSAGE_MS_R_UP: break; case MESSAGE_KEY_DOWN: switch (arg1) { case '\n': CleanUp; Init; break; case '0'...'9': next_action_key = arg1; break; case 'c': CenterMasses; break; case 'd': ODEPause(ode, OFF); ode->drag_v2 = 0.002; ode->drag_v3 = 0.0001; break; case 'v': edit_mode_bttn.state = EMD_MOVE; break; case 'm': edit_mode_bttn.state = EMD_MASS; break; case 'n': edit_mode_bttn.state = EMD_CONNECTOR; break; case 's': edit_mode_bttn.state = EMD_SPRING; break; case 't': edit_mode_bttn.state = EMD_THRUSTER; break; case 'z': Spawn(&Zoom, 2.0(I64)); break; case 'Z': Spawn(&Zoom, 0.5(I64)); break; case CH_SPACE: if (++main_mode_bttn.state == MMD_MODES_NUM) main_mode_bttn.state = 0; MessageGet(,, 1 << MESSAGE_KEY_UP); goto es_done; case CH_SHIFT_ESC: case CH_ESC: throw; } break; case MESSAGE_KEY_UP: switch (arg1) { case 'd': ODEPause(ode); ode->drag_v2 = 0.000002; ode->drag_v3 = 0.0000001; break; } break; } Refresh; } es_done: //Don't goto out of try okay = TRUE; } catch { Fs->catch_except = TRUE; okay = FALSE; } MenuPop; CtrlBttnDel(bt_edit_mode); if (!okay) throw; } U0 Strut() { CCtrl *bt_main_mode; SettingsPush; //See SettingsPush Fs->win_inhibit |= WIF_SELF_MS_L | WIF_SELF_MS_R | WIG_DBL_CLICK; AutoComplete; WinBorder; WinMax; DocCursor; DocClear; "\n$WW,1$$PURPLE$$TX+CX,\"Build a ship.\"$$FG$\n\n" "Sel mass mode.\tLeft-click to place masses.\n" "Sel spring mode.\tLeft-drag to make members.\n" "Sel thruster mode.\tPress a digit, 0-9. Drag to make thruster.\n" "Sel connector mode.\tPress a digit, 0-9. " "Drag to make breakable connector.\n\n" "Press $GREEN$<SPACE>$FG$ to run the game. Press digits to operate " "thrusters and break connectors.\n\n" "Press $GREEN$<z>$FG$ or $GREEN$<SHIFT-Z>$FG$ to zoom in/out.\n\n"; PressAKey; bt_main_mode = CtrlBttnNew((GR_WIDTH - 4 * FONT_WIDTH - 16) >> 1, 1 * FONT_HEIGHT + 4, 80,, MMD_MODES_NUM, Define("ST_MAIN_MODES"), main_mode_colors, &main_mode_bttn); PaletteSetLight(FALSE); DocClear; Init; Fs->draw_it = &DrawIt; try { while (TRUE) { EditShip; PlayShip; } } catch { CleanUp; Fs->catch_except = TRUE; } SettingsPop; CtrlBttnDel(bt_main_mode); }