From 3185e48ccdb7ae72333e3dfbb48e8d6a07056800 Mon Sep 17 00:00:00 2001 From: Justin Marshall Date: Sat, 11 Jul 2020 14:19:34 -0700 Subject: [PATCH] Added the ability to place new light objects in the world, lights don't work yet. --- code/CMakeLists.txt | 4 +- code/redalert/display.cpp | 2 + code/redalert/function.h | 1 + code/redalert/light.cpp | 129 ++++++++++++++++++++++++++++++++ code/redalert/light.h | 59 +++++++++++++++ code/redalert/mapedit.cpp | 95 ++++++++++++++--------- code/redalert/mapedit.h | 4 + code/redalert/scenario.cpp | 5 ++ code/redalert/sidebareditor.cpp | 10 ++- code/redalert/winstub.cpp | 2 + ui/edwin/lightbulb.png | Bin 0 -> 3003 bytes 11 files changed, 274 insertions(+), 37 deletions(-) create mode 100644 code/redalert/light.cpp create mode 100644 code/redalert/light.h create mode 100644 ui/edwin/lightbulb.png diff --git a/code/CMakeLists.txt b/code/CMakeLists.txt index 35c5bb5..d318cb8 100644 --- a/code/CMakeLists.txt +++ b/code/CMakeLists.txt @@ -631,7 +631,7 @@ set(src_redalert ./redalert/mapscript.cpp ./redalert/txtprnt.cpp ./redalert/tilesetxml.cpp - "redalert/sidebareditor.cpp") + "redalert/sidebareditor.cpp" "redalert/light.cpp") set(src_external #glew @@ -731,7 +731,7 @@ if (UNIX) add_definitions(-D RA_AF -DCHEAT_KEYS -DIMGUI_IMPL_OPENGL_LOADER_GLEW -DSCENARIO_EDITOR -DTRUE_FALSE_DEFINED -DENGLISH -D_USRDLL -DREDALERT_EXPORTS) endif(UNIX) -add_executable(RedAlert ${src_io} ${src_win32lib} ${src_redalert} ${src_external}) +add_executable(RedAlert ${src_io} ${src_win32lib} ${src_redalert} ${src_external} "redalert/light.cpp") if (MSVC) set_target_properties(RedAlert PROPERTIES OUTPUT_NAME "RedAlert" LINK_FLAGS "/PDB:\"RedAlert.pdb\" /SUBSYSTEM:WINDOWS" RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/../" ) diff --git a/code/redalert/display.cpp b/code/redalert/display.cpp index 079d256..d27d87c 100644 --- a/code/redalert/display.cpp +++ b/code/redalert/display.cpp @@ -2230,6 +2230,8 @@ ObjectClass * DisplayClass::Cell_Object(CELL cell, int x, int y) const HidPage.Unlock(); } + lightManager.RenderLights(); + #ifndef WIN32 /* ** Once the icons are drawn, duplicate the bottom line of the screen into the phantom diff --git a/code/redalert/function.h b/code/redalert/function.h index e4cbacf..3952823 100644 --- a/code/redalert/function.h +++ b/code/redalert/function.h @@ -234,6 +234,7 @@ struct NoInitClass { #include "crcstraw.h" #include "shastraw.h" #include "rndstraw.h" +#include "light.h" // Should be part of WWLIB.H. This is used in JSHELL.CPP. typedef struct { diff --git a/code/redalert/light.cpp b/code/redalert/light.cpp new file mode 100644 index 0000000..2f81c18 --- /dev/null +++ b/code/redalert/light.cpp @@ -0,0 +1,129 @@ +// light.cpp +// + +#include "function.h" +#include "image.h" + +void Light_t::PlaceLight(int screenx, int screeny) { + isPending = false; + COORDINATE lightCoord; + + CellClass::ConvertIsoCoordsToScreen(screenx, screeny); + + position = Map.Closest_Free_Spot(Map.Pixel_To_Coord(screenx, screeny)); +} + +bool Light_t::GetRenderPosition(int& x, int& y) { + Map.Coord_To_Pixel(position, x, y); + CellClass::ConvertCoordsToIsometric(x, y); + + return true; +} + + +LightManager lightManager; + +LightManager::LightManager() { + FreeAllLights(); +} + +void LightManager::Init(void) { + lightEditorIcon = Image_LoadImage("ui/edwin/lightbulb.png"); +} + +void LightManager::RenderLights(void) { + for(int i = 0; i < MAX_WORLD_LIGHTS; i++) { + if (!lights[i].active) { + continue; + } + + if(!lights[i].isPending) { + int screenx, screeny; + lights[i].GetRenderPosition(screenx, screeny); + GL_RenderImage(lightEditorIcon, screenx, screeny, 30, 30); + } + } +} + +void LightManager::FreeAllLights(void) { + memset(lights, 0, sizeof(lights)); +} + +Light_t* LightManager::AllocateLight(void) { + for(int i = 0; i < MAX_WORLD_LIGHTS; i++) { + if(!lights[i].active) { + lights[i].active = true; + lights[i].lightNum = i; + return &lights[i]; + } + } + + assert(!"Too many lights in the scene!"); + return NULL; +} + +Light_t *LightManager::PlaceLight(char *name, int x, int y, float r, float g, float b, float radius, LightType_t type) { + Light_t* light = AllocateLight(); + + if(!light) { + return NULL; + } + + if(name == NULL) { + sprintf(light->name, "light%d", light->lightNum); + } + else { + strcpy(light->name, name); + } + light->position = XYP_Coord(x, y); + light->color.x = r; + light->color.y = g; + light->color.z = b; + light->radius = radius; + light->type = type; + + return light; +} + +void LightManager::FreeLight(Light_t* light) { + light->active = false; +} + +void LightManager::Write_Scenerio_Lights(CCINIClass* ini) { + ini->Clear("Lights"); + + // Write out the lights. + for (int i = 0; i < MAX_WORLD_LIGHTS; i++) { + if (lights[i].active == false) { + continue; + } + + char buffer[2048]; + char name[256]; + sprintf(buffer, "%s,%d,%f,%f,%f,%f,%d", lights[i].name, lights[i].position, lights[i].color.x, lights[i].color.y, lights[i].color.z, lights[i].radius, lights[i].type); + sprintf(name, "%d", i); + ini->Put_String("Lights",name,buffer); + } +} + +void LightManager::Read_Scenerio_Lights(CCINIClass* ini) { + char buf[2048]; + + int len = ini->Entry_Count("Lights"); + for (int index = 0; index < len; index++) { + char const* entry = ini->Get_Entry("Lights", index); + + /* + ** Get a lighting entry. + */ + ini->Get_String("Lights", entry, NULL, buf, sizeof(buf)); + Light_t* light = AllocateLight(); + strcpy(light->name, strtok(buf, ",")); + light->position = atoi(strtok(NULL, ",")); + light->color.x = atof(strtok(NULL, ",")); + light->color.y = atof(strtok(NULL, ",")); + light->color.z = atof(strtok(NULL, ",")); + light->radius = atof(strtok(NULL, ",")); + light->type = (LightType_t)atoi(strtok(NULL, ",")); + } +} \ No newline at end of file diff --git a/code/redalert/light.h b/code/redalert/light.h new file mode 100644 index 0000000..2ebbc44 --- /dev/null +++ b/code/redalert/light.h @@ -0,0 +1,59 @@ +// light.h +// + +#include +#include "vectormath.h" + +class CCINIClass; +struct Image_t; + +#define MAX_WORLD_LIGHTS 6556 + +enum LightType_t { + LIGHT_AMBIENT = 0, + LIGHT_POINT, + LIGHT_SPOT +}; + +class Light_t { + friend class LightManager; +public: + int GetLightNum(void) { return lightNum; } + void PlaceLight(int screenx, int screeny); + bool GetRenderPosition(int& x, int& y); + + char name[512]; + unsigned long position; + float3 color; + float radius; + bool isPending; + LightType_t type; +protected: + int lightNum; + bool active; +}; + +class LightManager { +public: + LightManager(); + + void Init(void); + + Light_t* AllocateLight(void); + Light_t* PlaceLight(char *name, int x, int y, float r, float g, float b, float radius, LightType_t type); + + void FreeLight(Light_t* light); + void FreeAllLights(void); + + void Read_Scenerio_Lights(CCINIClass* ini); + void Write_Scenerio_Lights(CCINIClass* ini); +public: + Image_t* GetLightEditorIcon(void) { return lightEditorIcon; } + void RenderLights(void); +private: + Light_t lights[MAX_WORLD_LIGHTS]; + int numLights; + Image_t* lightEditorIcon; +}; + +extern LightManager lightManager; \ No newline at end of file diff --git a/code/redalert/mapedit.cpp b/code/redalert/mapedit.cpp index 8bc356c..048b488 100644 --- a/code/redalert/mapedit.cpp +++ b/code/redalert/mapedit.cpp @@ -107,6 +107,7 @@ MapEditClass::MapEditClass(void) // ScenVar = SCEN_VAR_A; LastHouse = HOUSE_GOOD; GrabbedObject = 0; + GrabbedLight = nullptr; Scen.Waypoint[WAYPT_HOME] = 0; CurrentCell = 0; CurTeam = NULL; @@ -545,25 +546,33 @@ void MapEditClass::AI(KeyNumType & input, int x, int y) rc = Mouse_Moved(); if (Keyboard->Down(KN_LMOUSE) && rc) { - - /* - ** "Paint" mode: place current object, and restart placement - */ - if (PendingObject) { - Flag_To_Redraw(true); - if (Place_Object() == 0) { - Changed = 1; - Start_Placement(); - } - } else { - + if (GrabbedLight) + { + GrabbedLight->PlaceLight(Get_Mouse_X(), Get_Mouse_Y()); + GrabbedLight = nullptr; + } + else + { /* - ** Move the currently-grabbed object + ** "Paint" mode: place current object, and restart placement */ - if (GrabbedObject) { - GrabbedObject->Mark(MARK_CHANGE); - if (Move_Grabbed_Object() == 0) { + if (PendingObject) { + Flag_To_Redraw(true); + if (Place_Object() == 0) { Changed = 1; + Start_Placement(); + } + } + else { + + /* + ** Move the currently-grabbed object + */ + if (GrabbedObject) { + GrabbedObject->Mark(MARK_CHANGE); + if (Move_Grabbed_Object() == 0) { + Changed = 1; + } } } } @@ -1466,27 +1475,40 @@ bool MapEditClass::Mouse_Moved(void) const ObjectTypeClass * objtype = NULL; bool retcode = false; - /* - ** Return if no motion - */ - if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { - return(false); + if (GrabbedLight != NULL) + { + GL_RenderImage(lightManager.GetLightEditorIcon(), Get_Mouse_X(), Get_Mouse_Y(), 32, 32); + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return true; } - - /* - ** Get a ptr to ObjectTypeClass - */ - if (PendingObject) { - objtype = PendingObject; - } else { - if (GrabbedObject) { - objtype = &GrabbedObject->Class_Of(); - } else { - old_mx = Get_Mouse_X(); - old_my = Get_Mouse_Y(); - old_zonecell = ZoneCell; + else + { + /* + ** Return if no motion + */ + if (old_mx == Get_Mouse_X() && old_my == Get_Mouse_Y()) { return(false); } + + /* + ** Get a ptr to ObjectTypeClass + */ + if (PendingObject) { + objtype = PendingObject; + } + else { + if (GrabbedObject) { + objtype = &GrabbedObject->Class_Of(); + } + else { + old_mx = Get_Mouse_X(); + old_my = Get_Mouse_Y(); + old_zonecell = ZoneCell; + return(false); + } + } } /* @@ -1659,6 +1681,11 @@ void MapEditClass::Main_Menu(void) process = false; break; + case 8: + GrabbedLight = lightManager.PlaceLight(NULL, 0, 0, 1.0f, 1.0f, 1.0f, 100, LIGHT_POINT); + GrabbedLight->isPending = true; + break; + /* ** Test-drive this scenario */ diff --git a/code/redalert/mapedit.h b/code/redalert/mapedit.h index d506f43..af18d73 100644 --- a/code/redalert/mapedit.h +++ b/code/redalert/mapedit.h @@ -49,6 +49,8 @@ #include #include "sidebareditor.h" +class Light_t; + /* ** This is the maximum # of ObjectTypeClasses the editor has to deal with. */ @@ -270,6 +272,8 @@ class MapEditClass : public MouseClass //int LastChoice; // index of item user picked last HousesType LastHouse; // house of last item picked + Light_t *GrabbedLight; + /* ** Variables for grabbing/moving objects */ diff --git a/code/redalert/scenario.cpp b/code/redalert/scenario.cpp index 6c16244..fabcc02 100644 --- a/code/redalert/scenario.cpp +++ b/code/redalert/scenario.cpp @@ -820,6 +820,8 @@ void Clear_Scenario(void) { // TCTCTC -- possibly just use in-place new of scenario object? + lightManager.FreeAllLights(); + Scen.MissionTimer = 0; Scen.MissionTimer.Stop(); Scen.Timer = 0; @@ -2289,6 +2291,8 @@ bool Read_Scenario_INI(char * fname, bool ) Scen.IsMoneyTiberium = ini.Get_Bool(BASIC, "FillSilos", Scen.IsMoneyTiberium); Scen.Percent = ini.Get_Int(BASIC, "Percent", Scen.Percent); + lightManager.Read_Scenerio_Lights(&ini); + /* ** Read in the specific information for each of the house types. This creates ** the houses of different types. @@ -2659,6 +2663,7 @@ void Write_Scenario_INI(char * fname) ini.Put_Bool(BASIC, "TruckCrate", Scen.IsTruckCrate); ini.Put_Int(BASIC, "Percent", Scen.Percent); + lightManager.Write_Scenerio_Lights(&ini); HouseClass::Write_INI(ini); TeamTypeClass::Write_INI(ini); TriggerTypeClass::Write_INI(ini); diff --git a/code/redalert/sidebareditor.cpp b/code/redalert/sidebareditor.cpp index aaad1a9..9df29f0 100644 --- a/code/redalert/sidebareditor.cpp +++ b/code/redalert/sidebareditor.cpp @@ -57,7 +57,8 @@ void SidebarEditor::draw_it(void) { if (ImGui::MenuItem("Scenario Options")) { mainmenu_selection = 5; } if (ImGui::MenuItem("AI Options")) { mainmenu_selection = 6; } ImGui::EndMenu(); - } + } + if (ImGui::BeginMenu("Objects")) { Debug_LockScroll = true; @@ -65,6 +66,13 @@ void SidebarEditor::draw_it(void) { ImGui::EndMenu(); } + if (ImGui::BeginMenu("Lights")) + { + Debug_LockScroll = true; + if (ImGui::MenuItem("Add Light")) { mainmenu_selection = 8; } + ImGui::EndMenu(); + } + if (ImGui::BeginMenu("Run")) { Debug_LockScroll = true; diff --git a/code/redalert/winstub.cpp b/code/redalert/winstub.cpp index 844c4a8..a264177 100644 --- a/code/redalert/winstub.cpp +++ b/code/redalert/winstub.cpp @@ -859,6 +859,8 @@ void Create_Main_Window ( HANDLE instance , int command_show , int width , int h io.Fonts->AddFontFromFileTTF("fonts/Arial.ttf", 16.0f); ImGui_NewFrame(); + + lightManager.Init(); } diff --git a/ui/edwin/lightbulb.png b/ui/edwin/lightbulb.png new file mode 100644 index 0000000000000000000000000000000000000000..139507741c0a1158a7bd3ffac0ccb4afc0bfaf35 GIT binary patch literal 3003 zcmb_e2~<;88h#i|BM{kK02dws6_o6IBq$IN&>#n*EN;BKyeyF9g(N`04I)JaLG7R@ zsD)C|x>U5(1r?EHtQ&5)%)tRHBT%u0)@pS`IxlRsbLiO9GdbtIcbETu|M&m@ckj*C z$cRNm8#fyO0El6sqA2XU(DD$hu{V%#ZW;ETpbL#N0D!Ht<*@>a_qhN7{+vn@Ym5~y z5x}U10?AM*LNRG{mPLTy0+SAclMo{)MHDKnkkotr5(!kvgrpd@m?qW*Axc$fh8~H| zh>*Y;NibhVTHp`*nFJVs1~EdQNu$;p1STPA5LbY;EyGk2IA~%_5|RQe4#8M)Bp8J1 z5s*WnkzpE*0dn~iI)}yN(0oA#jX|T*7*slwOrr~ETmgdt4*y7)H@!?Qh!TYi`@(uc zlG13@38>Vxv@}W@lY;6MR63u}w_q?BWXytWNY@%66IpBU96=Bv23W7s8C9qjv>-xK zG{q<+VN8cY(C9{CwT9s|VF{y}ARU!Xp;WjJhxwgHy3Eh7&TTwLy}uL#R=R0Zq}vNN_4f z)6)`-P7tI=AS0@mps0GJP>~~)K?W8H=pCcd%Fr}}&wC3H5oAP!BumxEG%lIOm(Xbf z23x@9U|j)?He@PBWh#04n5LEj|IQSXj0`eDA2gQ10y(PJKv=*k4WvM*I<0~Pj;2u% zgsM?J<``o~e}6nIC@4~o%2jG?LWCN@MUYC1sczOFQ?XbOrZpHLEsTVTgd{946qQOQ z;6QYQ4oPKXh{cqX5iW~Q=CNcPGA!dUrF0%c&g1Y$){9U$#ZrCC`k{>=LtzZ#H)c{6 zhtG%T3^EIbWMnp*MJG$8G#Q!4mcnudkH=y1kP)<{dKI=8A@%#H7FIG05n{4=EEX3c z)A;g(tj4(dh#!Iu3X zB*7LTATrYMuxgBa{3*}F>(i772Kqk)GUVNW%8hA|9tlujx%;31seeS?0HuC_ItSr0 zU>-!~^XPOki^;-@%D{3cWk{t=m`kHe<&2Tg$E3~?u-GGl{VUXm(*i3YtpdRgS1ReR z2lMB2ImB|zVBX(-|G!|UmNRuoVbuQ?-{9PP=Py>s!6EEWw=_p@6Rb0OA0b*SPkQXO znf@)f0syQJhlv6t%B=7Aiq(^sx#WDhcI}E5>Cw`0`5p&}%0%-u$LG)-) zhF6Bz(yMojPc-{hhKWT}8XWSshRmXPk@B1SraDcIjCS%RdF9+LJy&t~>FLK)*0yY* zbi#rKX`Ki5F0cNMo7a-wCG7pBiQl%5uPAn?;C^y%-^Rb!ZnJW@<>=$~rl7oP>D)Ee zxUg$}q0`K|Ynzt8HhJK0Y%bjv5n-y_Xyx*vbcWr@Z(<)-e&p0&0A6ShlMn7(dv?>LcPrbi+jiI{)HL=~^<7QD9UydPk(xXSam(LW_jbY= z>xo|(?>z6Ibe>(kX2uR`?ZSg@4%w?DJg>CZ#k}q11<<@sSN!vNN#(A9sS*EajLg4C z?Ry0}eK*{;S{CK&@d69bOl}YQ} z0+cCE<*u31){2l{{SOzOxZpK^^(kb^yq_rf+HeWZ`DEOL7PC+7Tx7kQXbalD$hvGk zOISN?uCce-E%u9|9vipJUdL?v8+LnYh$Xt7KA@>0uErc2`8&3$KgW`*ZCWG@0t56X5!iYFf4 zWev%@C0E0NQz3YKH}3YXvahx~A853Flhf{2pJ1Q$a(+dR|GnIklH<8eRxn`)=}<-) z{*^v6N*voj1L7#r#LL(1VkF6PfK5lzmfV}|5_2&USHHnYmy<|a*+aSCmItit*iJj8 zIy7@!LcZg}_^N&1e&X1oBgKDS)I;=I>Pa}^+Q~eSd)^_te*C>NBtpG&UgcHSvw1J$ z=V-!Xrxe$0W0@-#-ztip*0yl``5DeO{m+Ywf4VZ|R^T}KMA(-$(kK+^BA#kF