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 0000000..1395077 Binary files /dev/null and b/ui/edwin/lightbulb.png differ